แทรกข้อความด้วยเครื่องหมายคำพูดเดี่ยวใน PostgreSQL


432

test(id,name)ผมมีตาราง

user's logฉันจำเป็นต้องใส่ค่าที่ชอบ: 'my user', customer's,

 insert into test values (1,'user's log');
 insert into test values (2,''my users'');
 insert into test values (3,'customer's');

ฉันได้รับข้อผิดพลาดหากฉันเรียกใช้คำสั่งใด ๆ ข้างต้น

หากมีวิธีการใด ๆ ที่จะทำอย่างถูกต้องกรุณาแบ่งปัน ฉันไม่ต้องการข้อความใด ๆ ที่เตรียมไว้

เป็นไปได้โดยใช้กลไกการหลบหนี sql?


1
ใช้สิ่งที่คุ้มค่าที่จะหลบหนีห้องสมุดลูกค้าของคุณให้ สำหรับข้อมูลเพิ่มเติมคุณจะต้องบอกว่าคุณเข้าถึงฐานข้อมูลอย่างไร
Richard Huxton

ฐานข้อมูล @Richard Huxton เข้าถึงได้โดย java
MAHI

2
ดังนั้นใช้ตัวยึดมาตรฐาน jdbc หรืออธิบายว่าทำไมจึงไม่ใช่ตัวเลือกที่ดีที่สุด
Richard Huxton

@ Richard Huxton ฉันไม่ได้บอกว่าไม่ใช่ตัวเลือกที่ดีที่สุดฉันกำลังค้นหาว่ามีวิธีการหลบหนีใด ๆ ใน SQL ที่จะทำหรือไม่
MAHI

ดีเห็น @ ตอบ Claudix ด้านล่าง แต่เห็นได้ชัดว่าตัวอักษรค่าที่แตกต่างกันจะต้องหลบหนีขึ้นอยู่กับชนิดของพวกเขาpostgresql.org/docs/current/static/datatype.html
ริชาร์ด Huxton

คำตอบ:


763

สตริงตัวอักษร

หลีกเลี่ยงคำพูดเดียว'โดยเพิ่มเป็นสองเท่า -> ''เป็นวิธีมาตรฐานและผลงานแน่นอน:

'user's log'     -- incorrect syntax (unbalanced quote)
'user''s log'

ในเวอร์ชันเก่าหรือหากคุณยังคงรันด้วยstandard_conforming_strings = offหรือโดยทั่วไปถ้าคุณเติมสตริงของคุณด้วยEเพื่อประกาศไวยากรณ์สตริงการยกเว้น Posixคุณสามารถหลบหลีกด้วยแบ็กสแลช\:

E'user\'s log'

แบ็กสแลชตัวเองจะหนีด้วยแบ็กสแลชอีกตัว แต่โดยทั่วไปไม่เป็นที่นิยม
หากคุณต้องจัดการกับคำพูดเดียวหลาย ๆ คำหรือหลายเลเยอร์การหลบหนีคุณสามารถหลีกเลี่ยงการอ้างถึงนรกใน PostgreSQL ด้วยสตริงที่เสนอราคาดอลลาร์ :

'escape '' with '''''
$$escape ' with ''$$

เพื่อหลีกเลี่ยงความสับสนเพิ่มเติมระหว่างราคาดอลลาร์ให้เพิ่มโทเค็นที่ไม่ซ้ำกันในแต่ละคู่:

$token$escape ' with ''$token$

ซึ่งสามารถซ้อนหลายระดับ:

$token2$Inner string: $token1$escape ' with ''$token1$ is nested$token2$

ให้ความสนใจหาก$ตัวละครควรมีความหมายพิเศษในซอฟต์แวร์ไคลเอ็นต์ของคุณ คุณอาจต้องหลบหนีมันเพิ่มเติม นี่ไม่ใช่กรณีของไคลเอนต์ PostgreSQL มาตรฐานเช่น psql หรือ pgAdmin

นั่นคือทั้งหมดที่มีประโยชน์มากสำหรับการเขียนฟังก์ชั่น plpgsql หรือคำสั่ง ad-hoc SQL ไม่สามารถลดความจำเป็นในการใช้คำสั่งที่เตรียมไว้หรือวิธีอื่นเพื่อป้องกันการฉีด SQL ในแอปพลิเคชันของคุณเมื่อเป็นไปได้ที่ผู้ใช้ป้อนข้อมูล @ Craig ตอบได้มากกว่านี้ รายละเอียดเพิ่มเติม:

ค่าภายใน Postgres

เมื่อจัดการกับค่าในฐานข้อมูลมีฟังก์ชั่นที่มีประโยชน์สองอย่างเพื่ออ้างอิงสตริงอย่างถูกต้อง:

  • quote_literal()หรือquote_nullable() - หลังเอาท์พุทสตริงNULLสำหรับอินพุตว่าง (นอกจากนี้ยังมีquote_ident()การอ้างดับเบิลสตริงที่จำเป็นต้องได้รับ SQL ที่ถูกต้องตัวระบุ .)
  • format()ด้วยการระบุรูปแบบเทียบเท่ากับ%L ชอบ:quote_nullable()
    format('%L', string_var)
  • concat()หรือconcat_ws()โดยทั่วไปจะไม่ดีเนื่องจากคำเหล่านั้นไม่สามารถหลีกเลี่ยงคำพูดซ้อนและแบ็กสแลชเดี่ยว

1
นอกจากนี้ยังเป็นที่น่าสังเกตว่ารุ่น PgJDBC บางรุ่นมีปัญหาเกี่ยวกับการเสนอราคาดอลลาร์โดยเฉพาะอย่างยิ่งมันอาจล้มเหลวในการเพิกเฉยต่อคำสั่งสิ้นสุดของคำสั่ง (;) ภายในสตริงที่มีการอ้างอิงดอลลาร์
Craig Ringer

1
คำตอบที่เกี่ยวข้องนี้มีรายละเอียดสำหรับปัญหากับ JDBC
Erwin Brandstetter

1
และถ้าคุณต้องการที่จะหลบหนี s'tring จากคอลัมน์ข้อความในการแทรกในกรณีของภาษาขั้นตอน ฯลฯ คุณสามารถใช้ฟังก์ชันสตริง quote_literal (column_name)
alexglue

1
$ token $ ยอดเยี่ยม ขอบคุณ
mythicalcoder

@ErwinBrandstetter อีกครั้ง "สามารถซ้อนหลายระดับได้": แต่SELECT $outer$OUT$inner$INNER$inner$ER$outer$;พิสูจน์ได้ว่าการซ้อนในระดับที่ 2 ไม่ทำงานที่นี่
filiprem

46

นี่เป็นโลกที่แย่มากเพราะคำถามของคุณบอกเป็นนัยว่าคุณอาจมีช่องโหว่ในการฉีด SQLในแอปพลิเคชันของคุณ

คุณควรใช้คำสั่งที่กำหนดพารามิเตอร์ สำหรับ Java, ใช้กับตัวยึดPreparedStatement คุณบอกว่าคุณไม่ต้องการใช้ข้อความที่มีพารามิเตอร์ แต่คุณไม่ได้อธิบายว่าทำไมและตรงไปตรงมามันต้องมีเหตุผลที่ดีมากที่จะไม่ใช้มันเพราะมันเป็นวิธีที่ง่ายและปลอดภัยที่สุดในการแก้ไขปัญหาที่คุณพยายาม เพื่อแก้ปัญหา

ดูการป้องกัน SQL Injection ในชวา อย่าตกเป็นเหยื่อคนต่อไปของบ๊อบบี้

ไม่มีฟังก์ชั่นสาธารณะใน PgJDBC สำหรับการอ้างอิงสตริงและการหลบหนี ส่วนหนึ่งเป็นเพราะอาจทำให้ดูเหมือนเป็นความคิดที่ดี

มีอยู่ในตัว quoting ฟังก์ชั่นquote_literalและquote_identใน PostgreSQL แต่พวกเขาสำหรับฟังก์ชั่นการใช้งานPL/PgSQL EXECUTEวันนี้quote_literalไม่มีจำหน่ายแล้วโดยส่วนใหญ่EXECUTE ... USINGซึ่งเป็นรุ่น parameterisedเพราะมันปลอดภัยมากขึ้นและง่ายขึ้น คุณไม่สามารถใช้เพื่อจุดประสงค์ที่คุณอธิบายได้ที่นี่เนื่องจากเป็นฟังก์ชันฝั่งเซิร์ฟเวอร์


ลองจินตนาการว่าจะเกิดอะไรขึ้นถ้าคุณได้รับคุณค่า');DROP SCHEMA public;--จากผู้ใช้ที่เป็นอันตราย คุณจะผลิต:

insert into test values (1,'');DROP SCHEMA public;--');

ซึ่งแบ่งออกเป็นสองข้อความและความคิดเห็นที่ถูกละเว้น:

insert into test values (1,'');
DROP SCHEMA public;
--');

อ๊ะมีฐานข้อมูลของคุณอยู่


ฉันมักจะเห็นด้วยกับข้อยกเว้นหนึ่ง - "ที่" ส่วนคำสั่ง (แม้ว่าเขาจะพูดว่า "แทรก") กับรายการของค่าเป็นส่วนหนึ่งของข้อ "ใน" ประโยค (หรือพวงของ "หรือ" s) ฉันคิดว่าคุณสามารถนับขนาดของรายการและสร้างข้อความไปยังคำสั่งที่เตรียมไว้ด้วยประโยค "ใน" แต่มันก็แปลกในกรณีที่ใช้
Roboprog

@Roboprog ด้วยบางไดรเวอร์ไคลเอนต์คุณสามารถใช้= ANY(?)และพารามิเตอร์อาร์เรย์
Craig Ringer

12
ฉันมักจะใช้ตัวแทรกแบบนี้เพื่อบู๊ตข้อมูลควบคู่ไปกับ DDL ให้ลองตอบคำถามมากกว่าคำตอบเช่น 'คุณกำลังทำผิด'
ThatDataGuy

1
@ThatDataGuy ความคิดเห็นที่เป็นธรรม แต่ในคำถามนี้ OP ได้เพิ่มความคิดเห็นdatabase is accessed by javaเพื่อให้สิ่งนี้ตอบคำถามได้โดยตรง นอกจากนี้ยังเป็นสิ่งสำคัญมากสำหรับผู้ที่มาที่นี่เพื่อให้ตระหนักถึงอันตรายที่อาจเกิดขึ้นโดยเฉพาะอย่างยิ่งการฉีด SQL เป็นสาเหตุอันดับ 1 ของความเสี่ยงด้านซอฟต์แวร์ เมื่อทราบถึงปัญหาแล้วผู้คนสามารถตัดสินใจได้อย่างชาญฉลาดว่าอะไรจะไม่เกิดขึ้นเช่นการใช้ประโยชน์จาก bootstrapping ของคุณ
Davos

เผง ผู้คนก็คัดลอกและวางรหัสเป็นจำนวนมาก ฉันจะหยุดเตือนผู้คนเกี่ยวกับเรื่องนี้ในวันที่ฉันหยุดเห็นช่องโหว่การฉีด SQL ทุกวันในรหัสการผลิต
Craig Ringer

26

ตามเอกสารของ PostgreSQL (4.1.2.1. ค่าคงที่สตริง) :

 To include a single-quote character within a string constant, write two 
 adjacent single quotes, e.g. 'Dianne''s horse'.

ดูเพิ่มเติมที่พารามิเตอร์standard_conforming_stringsซึ่งควบคุมว่าการหลบหลีกด้วยแบ็กสแลชทำงานได้หรือไม่


ขอบคุณสำหรับการตอบกลับ แต่ฉันต้องยกเว้นถ่านด้วยตนเองโดยใช้สิ่งนี้ถ้ามันมีฟังก์ชั่นในตัวสำหรับการทำเช่นนี้?
MAHI

3
@MAHI หากมีฟังก์ชั่นดังกล่าวมันจะอยู่ใน PgJDBC ไม่ใช่ใน PostgreSQL ของตัวเองเพราะการหลบหนีจะต้องทำในฝั่งไคลเอ็นต์ ไม่มีฟังก์ชั่นสาธารณะที่มีเอกสารดังกล่าวเพราะมันเป็นความคิดที่แย่มาก คุณควรใช้ข้อความที่มีพารามิเตอร์ดังนั้นคุณไม่จำเป็นต้องหลบหนีใด ๆ ที่อาจไม่น่าเชื่อถือ
Craig Ringer

13

ใน postgresql ถ้าคุณต้องการแทรกค่าลงไป'ในนั้นคุณต้องให้ค่าพิเศษ'

 insert into test values (1,'user''s log');
 insert into test values (2,'''my users''');
 insert into test values (3,'customer''s');

upvote สำหรับการแสดงราคาสามเท่าถ้าคุณมีสตริงที่ยกมา
winkbrace

ขึ้นเพราะเป็นวิธีแก้ปัญหาง่ายๆ
ktaria


2

หากคุณต้องการทำงานให้สำเร็จภายใน Pg:

to_json(value)

https://www.postgresql.org/docs/9.3/static/functions-json.html#FUNCTIONS-JSON-TABLE


คำถามนี้เกี่ยวข้องกับ JSON อย่างไร
Erwin Brandstetter

1
@ErwinBrandstetter ขอโทษผมอาจจะออก .. แต่มันหนีคำพูดในสตริง
hatenine

1
นั่นเป็นอีกเรื่องหนึ่งโดยสิ้นเชิง คุณอาจใช้format(), quote_literal()หรือquote_nullable()สำหรับราคาการหลบหนี ดู: stackoverflow.com/a/25143945/939860
Erwin Brandstetter

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.