簡體   English   中英

使用PCRE正則表達式驗證PHP中的SQL時發生災難性的回溯

[英]Catastrophic backtracking when using PCRE regexp to validate SQL in PHP

我正在嘗試以常規形式驗證一些SQL:

UPDATE `mytable` SET `keyname` = 'keyvalue',
       `a` = 'somestring',
       `b` = 123,
       `c` = NULL
       WHERE `keyname` = 'keyvalue'

領域比這更多。 值將為字符串,整數或NULL。

我原來的正則表達式是這樣的:

(?ix)

^

\s*
UPDATE \s+ `mytable` \s+
SET \s+ `keyname` \s = \s 'keyvalue'
(, \s+
  `[A-Z_]+`  (?# field name)
  \s+ = \s+  (?# equals value)
  (
  -?[0-9]+         (?# an integer, possibly negative)
  |
  '(\\.|''|[^'])*' (?# a string in single quotes)
  |
  NULL             (?# NULL)
  )
)+    (?# one or more such assignments)

\s+ WHERE \s+ `keyname` \s+ = \s+ 'keyvalue'

$

這工作到一定程度。 根據https://regex101.com/,它以180步進行匹配。

不幸的是,真正的SQL比這更長,例如:

UPDATE `mytable`
SET `keyname` = 'keyvalue',
`Markup` =
'Lorem ipsum dolor sit amet, consectetur adipiscing elit.
''Quisque vel mattis odio, quis iaculis sem.''
Nulla facilisi.
Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere
cubilia Curae; Fusce ut dui venenatis, maximus lorem eget, ornare ex.
Aenean tempus pulvinar est, id fringilla enim sagittis id. Mauris finibus
cursus commodo.\r\n\r\n
Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere
cubilia Curae; Fusce ut dui venenatis, maximus lorem eget, ornare ex.
Aenean tempus pulvinar est, id fringilla enim sagittis id. Mauris finibus
cursus commodo.\r\n\r\n\r\n
\'Aenean in augue a est vulputate accumsan.\'
Phasellus nulla diam, laoreet a elit non, mattis finibus magna. Phasellus
faucibus iaculis mi sed pulvinar.\r\n
Aliquam non nisl ultricies, aliquam augue vitae, efficitur sapien.
Etiam viverra, magna a laoreet sollicitudin, ipsum erat tincidunt sem, nec
faucibus enim tortor eget massa.
Nunc nisi orci, lacinia vitae dictum et, vestibulum sed metus. ',
`From_Date` = NULL,
`To_Date` = NULL,
`Foo` = '',
`Box_Colour` = NULL,
`Modification_Date` = '2016-09-08 12:30:47',
`Modified_User` = 1,
`Modified_IP` = '192.168.1.1'
WHERE `keyname` = 'keyvalue'

現在需要4301步。 實際上,如果您將Lorem Ipsum增大,我們的步數將超過20000。

另外,如果我們引入錯誤(使其不匹配),例如更改:

`Foo` = '',

`Foo` = ''

現在,它因災難性的回溯而崩潰。

通過使內部組(鍵/值對)成為原子組,可以在某種程度上擺脫災難性的回溯。 也就是說,更改:

SET \s+ `keyname` \s = \s 'keyvalue'
(, \s+

SET \s+ `keyname` \s = \s 'keyvalue'
(?>, \s+

在目標Web服務器上運行時,真實數據上的20000+步驟導致PHP腳本崩潰。 我需要將步驟降低到一些更現實的價值。 當regexp相當明確時,我不太明白為什么會有這么多回溯。 擺弄所有格修飾符或原子組似乎什么都不做,或者導致“通過”或“失敗” SQL不能正確匹配。

編輯:對於字符串子表達式:
使用@NikiC的展開循環版本,
在需要時用\\s*替換\\s
並添加一個額外的原子團,
它歸結為合理的步驟。

https://regex101.com/r/yV5xI7/3

編輯:您也可以嘗試此版本而不會展開循環'(?>[^'\\\\]+|\\\\.|'')*'相同的區別。https://regex101.com/r/yV5xI7/5

 (?si)

 ^ 

 \s* 
 UPDATE \s+ `mytable` \s+ 
 SET \s+ `keyname` \s* = \s* 'keyvalue'

 (?# one or more such key = value  )
 (?>
      \s* , \s* 

      (?# field name )
      ` [A-Z_]+ `  

      (?# equals )
      \s* = \s*  

      (?# value )
      (?>
           (?# an integer, possibly negative )
           -? [0-9]+         
        |  
           (?# or, a string )
           '
           [^'\\]* 
           (?:
                (?: \\ . | '' )
                [^'\\]* 
           )*
           '

           # '
           # (?: [^'\\] | '' | \\ . )*
           # ' 

        |  
           (?# or, literal NULL )
           NULL             
      )
 )+

 \s+ WHERE \s+ `keyname` \s* = \s* 'keyvalue'

 $

您可以嘗試將量化的核心包裝在一個原子組中。
另外,您可能需要dot-all修飾符來引用dot .

並且,如果您將轉義符與其中之一匹配,則應排除轉義符
字符串子表達式中的替代形式。
已知引擎會采用匹配的路徑。
在這種情況下, [^']也匹配轉義符,看起來像
轉義不應該被允許單獨存在。
並且,實際上可能存在有效的轉義符+換行符序列,因此(?s)
即使用(?: \\\\ . | '' | [^'\\\\] )*

放在一起,這是一個理想的選擇

/
     (?si)
     ^ \s* UPDATE \s+ `mytable` \s+ SET \s+ `keyname` \s = \s 'keyvalue'
     (?>
          , \s+ ` [A-Z_]+ `
          (?# field name )
          \s+ = \s+ 
          (?# equals value )
          (?:
               -? [0-9]+ 
               (?# an integer, possibly negative )
            |  '
               (?: \\ . | '' | [^'\\] )*
               '
               (?# a string in single quotes )
            |  NULL
               (?# NULL )
          )
     )+
     (?# one or more such assignments )
     \s+ WHERE \s+ `keyname` \s+ = \s+ 'keyvalue' $
/x

和一個為PHP

'/
     (?si)
     ^ \s* UPDATE \s+ `mytable` \s+ SET \s+ `keyname` \s = \s \'keyvalue\'
     (?>
          , \s+ ` [A-Z_]+ `
          (?# field name )
          \s+ = \s+ 
          (?# equals value )
          (?:
               -? [0-9]+ 
               (?# an integer, possibly negative )
            |  \'
               (?: \\\ . | \'\' | [^\'\\\] )*
               \'
               (?# a string in single quotes )
            |  NULL
               (?# NULL )
          )
     )+
     (?# one or more such assignments )
     \s+ WHERE \s+ `keyname` \s+ = \s+ \'keyvalue\' $
/x'

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM