วิธีจัดเก็บข้อมูลใน S3 และอนุญาตให้ผู้ใช้เข้าถึงอย่างปลอดภัยด้วยไคลเอนต์ Rails API / iOS


93

ฉันเพิ่งเริ่มเขียน Rails และ API ฉันต้องการความช่วยเหลือเกี่ยวกับโซลูชันพื้นที่จัดเก็บ S3 นี่คือปัญหาของฉัน

ฉันกำลังเขียน API สำหรับแอพ iOS ที่ผู้ใช้ล็อกอินด้วย Facebook API บน iOS เซิร์ฟเวอร์ตรวจสอบผู้ใช้กับปัญหาโทเค็น Facebook ให้กับผู้ใช้ iOS และออกโทเค็นเซสชันชั่วคราว จากจุดนี้ผู้ใช้ต้องดาวน์โหลดเนื้อหาที่เก็บไว้ใน S3 เนื้อหานี้เป็นของผู้ใช้และส่วนย่อยของเพื่อนของเขาเท่านั้น ผู้ใช้รายนี้สามารถเพิ่มเนื้อหาเพิ่มเติมใน S3 ซึ่งคนกลุ่มเดียวกันสามารถเข้าถึงได้ เดาว่าคล้ายกับการแนบไฟล์ในกลุ่ม Facebook ...

มี 2 ​​วิธีที่ผู้ใช้สามารถโต้ตอบกับ S3 ... ปล่อยไว้ที่เซิร์ฟเวอร์หรือให้เซิร์ฟเวอร์ออกโทเค็น S3 ชั่วคราว (ไม่แน่ใจในความเป็นไปได้ที่นี่) และผู้ใช้สามารถเข้าถึง URL เนื้อหาไปยัง S3 ได้โดยตรง ฉันพบว่าคำถามนี้พูดถึงแนวทาง แต่มันล้าสมัยไปแล้ว (2 ปีที่แล้ว): คำถามเกี่ยวกับสถาปัตยกรรมและการออกแบบเกี่ยวกับการอัปโหลดรูปภาพจากแอพ iPhone และ S3

ดังนั้นคำถาม:

  • มีวิธี จำกัด ผู้ใช้ในการเข้าถึงเนื้อหาบางส่วนบน S3 เมื่อมีการออกโทเค็นชั่วคราวหรือไม่? ฉันจะทำเช่นนี้ได้อย่างไร? สมมติว่ามี ... พูดว่าผู้ใช้ 100,000 คนขึ้นไป
  • เป็นความคิดที่ดีหรือไม่ที่จะให้อุปกรณ์ iOS ดึงเนื้อหานี้ออกโดยตรง
  • หรือควรปล่อยให้เซิร์ฟเวอร์ควบคุมการส่งผ่านเนื้อหาทั้งหมด (แน่นอนว่าเป็นการแก้ปัญหาความปลอดภัย)? นี่หมายความว่าฉันต้องดาวน์โหลดเนื้อหาทั้งหมดไปยังเซิร์ฟเวอร์ก่อนที่จะส่งมอบให้กับผู้ใช้ที่เชื่อมต่อ?
  • หากคุณรู้จักราง ... ฉันสามารถใช้คลิปหนีบกระดาษและอัญมณี aws-sdk เพื่อตั้งค่าแบบนี้ได้หรือไม่?

ขออภัยสำหรับหลายคำถามและขอขอบคุณสำหรับข้อมูลเชิงลึกเกี่ยวกับปัญหา ขอบคุณ :)


1
พบสิ่งนี้และคิดว่าฉันจะแสดงความคิดเห็นสำหรับคนอื่นที่กำลังมองหาdocs.aws.amazon.com/AmazonS3/latest/dev/…
dibble

คำตอบ:


113

เมื่อใช้อัญมณี aws-sdkคุณจะได้รับ url ที่ลงชื่อชั่วคราวสำหรับวัตถุ S3 ใด ๆ โดยเรียกurl_for:

s3 = AWS::S3.new(
  :access_key_id => 1234,
  :secret_access_key => abcd
)
object = s3.buckets['bucket'].objects['path/to/object']
object.url_for(:get, { :expires => 20.minutes.from_now, :secure => true }).to_s

สิ่งนี้จะให้ URL ใช้งานชั่วคราวที่เซ็นชื่อสำหรับออบเจ็กต์นั้นใน S3 เท่านั้น มันจะหมดอายุหลังจาก 20 นาที (ในตัวอย่างนี้) และใช้ได้ดีกับออบเจ็กต์เดียวเท่านั้น

หากคุณมีวัตถุจำนวนมากที่ลูกค้าต้องการคุณจะต้องออก URL ที่มีการลงชื่อจำนวนมาก

หรือควรปล่อยให้เซิร์ฟเวอร์ควบคุมการส่งผ่านเนื้อหาทั้งหมด (แน่นอนว่าเป็นการแก้ปัญหาความปลอดภัย)? นี่หมายความว่าฉันต้องดาวน์โหลดเนื้อหาทั้งหมดไปยังเซิร์ฟเวอร์ก่อนที่จะส่งมอบให้กับผู้ใช้ที่เชื่อมต่อ?

โปรดทราบว่านี่ไม่ได้หมายความว่าเซิร์ฟเวอร์จำเป็นต้องดาวน์โหลดแต่ละอ็อบเจ็กต์ แต่จะต้องพิสูจน์ตัวตนและอนุญาตให้ไคลเอ็นต์เฉพาะเข้าถึงอ็อบเจ็กต์เฉพาะใน S3

เอกสาร API จาก Amazon: https://docs.aws.amazon.com/AmazonS3/latest/dev/RESTAuthentication.html#RESTAuthenticationQueryStringAuth


3
ขอบคุณที่ @ejdyksen วิธีแก้ปัญหาที่ฉันคิดว่าใช้อย่างนั้น (ฉันยังไม่ได้อัปเดตคำถามด้วยคำตอบของฉัน)! ดังนั้นวิธีแก้ปัญหาของฉันคือทำ URL ที่พิสูจน์ตัวตนสำหรับคำขอ GET อย่างไรก็ตามเมื่อผู้ใช้มีส่วนร่วมในเนื้อหาเขาจะสร้างทรัพยากรไปยังตำแหน่งเฉพาะ / bucket / user / objectname โดยใช้โทเค็น IAM แบบรวม (ข้อมูลรับรองชั่วคราวที่หมดอายุ) พร้อมกับนโยบายที่แนบมาเพื่ออนุญาตให้ / bucket / user / * write access ดังนั้นจึงไม่มีผู้ใช้ในระบบที่ทำอันตรายต่อเนื้อหาของผู้ใช้รายอื่น ดูเหมือนว่าจะทำงานได้ดี ชื่นชมคำตอบของคุณ
dineth

5
หากคุณใช้ v2 ของ aws-sdk-ruby โปรดทราบว่าวิธีการจะแตกต่างกันบ้าง: docs.aws.amazon.com/sdkforruby/api/Aws/S3/…
vijucat

2
ไม่ใช่ความเสี่ยงที่ผู้ใช้จะเห็นคีย์การเข้าถึงของฉัน? นอกจากนี้ยังมีคีย์ลับ (แปลงแล้ว)
user2503775

3
@ เดนนิสจุดรวมคือไฟล์ไม่ต้องไปโดนเซิร์ฟเวอร์ของคุณ ลิงก์ไม่มีข้อมูลรับรอง AWS ของคุณ มันอาจจะมีACCESS_KEY_ID(จำไม่ได้ว่าปิดด้านบนหัว) แต่ไม่ได้ตั้งใจให้เป็นความลับ
ejdyksen

2
@ejdyksen คุณพูดถูกฉันเพิ่งยืนยันว่า URL มีเฉพาะไฟล์AWS_ACCESS_KEY_ID. เดิมทีฉันคิดว่าAWS_SECRET_ACCESS_KEYมีการแสดงด้วย แต่มันไม่ใช่
Dennis

47

คำตอบข้างต้นใช้อัญมณี aws-sdk-v1 แบบเก่าแทนที่จะเป็น aws-sdk-resources เวอร์ชัน 2 ใหม่

วิธีใหม่คือ:

aws_resource = Aws::S3::Resource::new
aws_resource.bucket('your_bucket').object('your_object_key').presigned_url(:get, expires_in: 1*20.minutes)

โดย your_object_key เป็นเส้นทางไปยังไฟล์ของคุณ หากคุณต้องการค้นหาคุณจะใช้สิ่งต่อไปนี้

s3 = Aws::S3::Client::new
keys = []
s3.list_objects(bucket: 'your_bucket', prefix: 'your_path').contents.each { |e| 
  keys << e.key
}

ข้อมูลนั้นยากมากที่จะขุดขึ้นมาและฉันเกือบจะยอมแพ้และใช้อัญมณีที่มีอายุมากกว่า

ข้อมูลอ้างอิง

http://docs.aws.amazon.com/sdkforruby/api/Aws/S3/Object.html#presigned_url-instance_method


1
expires_inคาดว่าข้อมูลจะอยู่ในรูปของวินาทีเพียงตรวจสอบให้แน่ใจว่าได้แปลงข้อมูลนั้นก่อน
frillybob

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