การอัพโหลดไฟล์โดยตรงของ Amazon S3 จากเบราว์เซอร์ไคลเอ็นต์ - การเปิดเผยคีย์ส่วนตัว


159

ฉันกำลังใช้การอัปโหลดไฟล์โดยตรงจากเครื่องไคลเอนต์ไปยัง Amazon S3 ผ่าน REST API โดยใช้ JavaScript เท่านั้นโดยไม่มีรหัสฝั่งเซิร์ฟเวอร์ ทำงานได้ดี แต่มีสิ่งหนึ่งที่ทำให้ฉันกังวล ...

เมื่อฉันส่งคำขอไปยัง Amazon S3 REST API ฉันต้องลงชื่อคำขอและใส่ลายเซ็นในAuthenticationส่วนหัว ในการสร้างลายเซ็นฉันต้องใช้รหัสลับของฉัน แต่ทุกสิ่งเกิดขึ้นในฝั่งไคลเอ็นต์ดังนั้นคีย์ลับสามารถเปิดเผยได้อย่างง่ายดายจากแหล่งที่มาของหน้าเว็บ (แม้ว่าฉันจะทำให้งงงวย / เข้ารหัสแหล่งที่มาของฉัน)

ฉันจะจัดการสิ่งนี้ได้อย่างไร และมันเป็นปัญหาหรือเปล่า? บางทีฉันสามารถ จำกัด การใช้คีย์ส่วนตัวที่เจาะจงเฉพาะกับการเรียกใช้ REST API จาก CORS Origin ที่เฉพาะเจาะจงและเฉพาะวิธี PUT และ POST หรืออาจเชื่อมโยงคีย์ไปที่ S3 เท่านั้นและที่เก็บข้อมูลเฉพาะ อาจจะมีวิธีการรับรองความถูกต้องอื่นได้หรือไม่

โซลูชัน "Serverless" นั้นเหมาะสมที่สุด แต่ฉันสามารถพิจารณาเกี่ยวกับการประมวลผลเซิร์ฟเวอร์บางตัวได้ยกเว้นการอัปโหลดไฟล์ไปยังเซิร์ฟเวอร์ของฉันแล้วส่งไปยัง S3


7
ง่ายมาก: อย่าเก็บความลับของลูกค้า คุณจะต้องเกี่ยวข้องกับเซิร์ฟเวอร์เพื่อลงนามคำขอ
เรย์นิโคลัส

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

5
มันเป็นปี 2559 เนื่องจากสถาปัตยกรรมที่ไม่มีเซิร์ฟเวอร์เป็นที่นิยมการอัพโหลดไฟล์ โดยตรงไปยัง S3 นั้นเป็นไปได้ด้วยความช่วยเหลือของ AWS Lambda ดูคำตอบของฉันสำหรับคำถามที่คล้ายกัน: stackoverflow.com/a/40828683/2504317โดยทั่วไปคุณจะมีฟังก์ชั่นแลมบ์ดาเป็น URL ที่สามารถอัปโหลด API ได้สำหรับการลงนาม API สำหรับแต่ละไฟล์และจาวาสคริปต์ทางไคลเอนต์ของคุณเพียงแค่ทำ HTTP URL ที่ลงชื่อล่วงหน้า ฉันได้เขียนองค์ประกอบ Vue ที่ทำสิ่งต่าง ๆ เช่นรหัสอัพโหลด S3 ที่เกี่ยวข้องกับผู้ไม่เชื่อเรื่องพระเจ้ามีรูปลักษณ์และได้รับแนวคิด
KF Lin

อีกบุคคลที่สามสำหรับการอัปโหลด HTTP / S POST ในที่ฝากข้อมูล S3 JS3Upload pure HTML5: jfileupload.com/products/js3upload-html5/index.html
JFU

คำตอบ:


216

ฉันคิดว่าสิ่งที่คุณต้องการคือการอัปโหลดด้วยเบราว์เซอร์โดยใช้ POST

โดยพื้นฐานแล้วคุณต้องใช้รหัสฝั่งเซิร์ฟเวอร์ แต่ทั้งหมดนี้ก็คือสร้างนโยบายที่ลงชื่อ เมื่อรหัสฝั่งไคลเอ็นต์มีนโยบายที่ลงชื่อแล้วก็สามารถอัปโหลดโดยใช้ POST ไปยัง S3 โดยตรงโดยไม่ต้องมีข้อมูลผ่านเซิร์ฟเวอร์ของคุณ

นี่คือลิงค์เอกสารทางการ:

ไดอะแกรม: http://docs.aws.amazon.com/AmazonS3/latest/dev/UsingHTTPPOST.html

รหัสตัวอย่าง: http://docs.aws.amazon.com/AmazonS3/latest/dev/HTTPPOSTExamples.html

นโยบายที่ลงนามแล้วจะเป็น html ของคุณในรูปแบบดังนี้:

<html>
  <head>
    ...
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    ...
  </head>
  <body>
  ...
  <form action="http://johnsmith.s3.amazonaws.com/" method="post" enctype="multipart/form-data">
    Key to upload: <input type="input" name="key" value="user/eric/" /><br />
    <input type="hidden" name="acl" value="public-read" />
    <input type="hidden" name="success_action_redirect" value="http://johnsmith.s3.amazonaws.com/successful_upload.html" />
    Content-Type: <input type="input" name="Content-Type" value="image/jpeg" /><br />
    <input type="hidden" name="x-amz-meta-uuid" value="14365123651274" />
    Tags for File: <input type="input" name="x-amz-meta-tag" value="" /><br />
    <input type="hidden" name="AWSAccessKeyId" value="AKIAIOSFODNN7EXAMPLE" />
    <input type="hidden" name="Policy" value="POLICY" />
    <input type="hidden" name="Signature" value="SIGNATURE" />
    File: <input type="file" name="file" /> <br />
    <!-- The elements after this will be ignored -->
    <input type="submit" name="submit" value="Upload to Amazon S3" />
  </form>
  ...
</html>

สังเกตว่าการกระทำของ FORM กำลังส่งไฟล์ไปยัง S3 โดยตรงไม่ใช่ผ่านเซิร์ฟเวอร์ของคุณ

ทุกครั้งที่ผู้ใช้ของคุณต้องการอัปโหลดไฟล์คุณจะต้องสร้างPOLICYและSIGNATUREบนเซิร์ฟเวอร์ของคุณ คุณกลับหน้าไปยังเบราว์เซอร์ของผู้ใช้ ผู้ใช้สามารถอัพโหลดไฟล์ไปยัง S3 ได้โดยตรงโดยไม่ต้องผ่านเซิร์ฟเวอร์ของคุณ

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

ข้อมูลเฉพาะที่ไปหรือจากเซิร์ฟเวอร์ของคุณคือ URL ที่ลงชื่อ รหัสลับของคุณยังคงเป็นความลับบนเซิร์ฟเวอร์


14
โปรดทราบว่านี่ใช้ Signature v2 ซึ่งจะถูกแทนที่ด้วย v4: docs.aws.amazon.com/AmazonS3/latest/API/…
Jörn Berkefeld

9
ให้มากแน่ใจว่าจะเพิ่ม${filename}ชื่อคีย์ดังนั้นสำหรับตัวอย่างข้างต้นแทนเพียงuser/eric/${filename} user/ericหากuser/ericเป็นโฟลเดอร์ที่มีอยู่แล้วการอัปโหลดจะล้มเหลวในไม่ช้า (คุณจะถูกเปลี่ยนเส้นทางไปยัง success_action_redirect) และเนื้อหาที่อัปโหลดจะไม่อยู่ที่นั่น เพียงแค่ใช้เวลาหลายชั่วโมงในการดีบักการคิดนี้เป็นปัญหาสิทธิ์
Balint Erdi

@secretmike หากคุณได้รับการหมดเวลาจากการทำวิธีนี้คุณจะแนะนำให้วนรอบการทำอย่างไร
เดินทาง

1
@Trip เนื่องจากเบราว์เซอร์กำลังส่งไฟล์ไปยัง S3 คุณจะต้องตรวจสอบการหมดเวลาใน Javascript และเริ่มลองใหม่อีกครั้ง
secretmike

@secretmike ที่มีกลิ่นเหมือนวงวนไม่สิ้นสุด เนื่องจากการหมดเวลาจะเกิดขึ้นอีกอย่างไม่มีกำหนดสำหรับไฟล์ใด ๆ ที่มากกว่า 10 / mbs
เดินทาง

40

คุณสามารถทำได้โดย AWS S3 Cognito ลองลิงค์นี้ที่นี่:

http://docs.aws.amazon.com/AWSJavaScriptSDK/guide/browser-examples.html#Amazon_S3

ลองใช้รหัสนี้

เพียงเปลี่ยนภูมิภาค IdentityPoolId และชื่อที่เก็บข้อมูลของคุณ

<!DOCTYPE html>
<html>

<head>
    <title>AWS S3 File Upload</title>
    <script src="https://sdk.amazonaws.com/js/aws-sdk-2.1.12.min.js"></script>
</head>

<body>
    <input type="file" id="file-chooser" />
    <button id="upload-button">Upload to S3</button>
    <div id="results"></div>
    <script type="text/javascript">
    AWS.config.region = 'your-region'; // 1. Enter your region

    AWS.config.credentials = new AWS.CognitoIdentityCredentials({
        IdentityPoolId: 'your-IdentityPoolId' // 2. Enter your identity pool
    });

    AWS.config.credentials.get(function(err) {
        if (err) alert(err);
        console.log(AWS.config.credentials);
    });

    var bucketName = 'your-bucket'; // Enter your bucket name
    var bucket = new AWS.S3({
        params: {
            Bucket: bucketName
        }
    });

    var fileChooser = document.getElementById('file-chooser');
    var button = document.getElementById('upload-button');
    var results = document.getElementById('results');
    button.addEventListener('click', function() {

        var file = fileChooser.files[0];

        if (file) {

            results.innerHTML = '';
            var objKey = 'testing/' + file.name;
            var params = {
                Key: objKey,
                ContentType: file.type,
                Body: file,
                ACL: 'public-read'
            };

            bucket.putObject(params, function(err, data) {
                if (err) {
                    results.innerHTML = 'ERROR: ' + err;
                } else {
                    listObjs();
                }
            });
        } else {
            results.innerHTML = 'Nothing to upload.';
        }
    }, false);
    function listObjs() {
        var prefix = 'testing';
        bucket.listObjects({
            Prefix: prefix
        }, function(err, data) {
            if (err) {
                results.innerHTML = 'ERROR: ' + err;
            } else {
                var objKeys = "";
                data.Contents.forEach(function(obj) {
                    objKeys += obj.Key + "<br>";
                });
                results.innerHTML = objKeys;
            }
        });
    }
    </script>
</body>

</html>

สำหรับรายละเอียดเพิ่มเติมกรุณาตรวจสอบ - Github

สิ่งนี้รองรับหลายภาพหรือไม่?
2722667

@ user2722667 ใช่
Joomler

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

1
@usama คุณช่วยกรุณาเปิดปัญหาใน GitHub ได้เพราะปัญหายังไม่ชัดเจนสำหรับฉัน
Joomler

@Joomler ขออภัยที่ตอบช้าฉันได้เปิดปัญหาที่ GitHub โปรดดูที่ขอบคุณนี้ github.com/aws/aws-sdk-php/issues/1332
usama

16

คุณกำลังบอกว่าคุณต้องการโซลูชัน "ไร้เซิร์ฟเวอร์" แต่นั่นหมายความว่าคุณไม่สามารถใส่รหัส "ของคุณ" ลงในลูปได้ (หมายเหตุ: เมื่อคุณให้รหัสแก่ลูกค้าแล้วรหัส "ตอนนี้" ของพวกเขา) การล็อค CORS จะไม่ช่วย: ผู้คนสามารถเขียนเครื่องมือที่ไม่ใช่เว็บ (หรือพร็อกซี่บนเว็บ) ได้อย่างง่ายดาย ส่วนหัว CORS ที่ถูกต้องเพื่อละเมิดระบบของคุณ

ปัญหาใหญ่คือคุณไม่สามารถแยกความแตกต่างระหว่างผู้ใช้ที่แตกต่างกัน คุณไม่สามารถอนุญาตให้ผู้ใช้รายหนึ่งรายการ / เข้าถึงไฟล์ของเขา แต่ป้องกันไม่ให้ผู้อื่นทำเช่นนั้น หากคุณตรวจพบการละเมิดไม่มีอะไรที่คุณสามารถทำได้ยกเว้นเปลี่ยนรหัส (ซึ่งผู้โจมตีสามารถสันนิษฐานได้อีกครั้ง)

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

หากคุณมีเซิร์ฟเวอร์ (แม้แต่อินสแตนซ์ขนาดเล็กที่ราคา $ 20 / เดือน) คุณสามารถเซ็นกุญแจบนเซิร์ฟเวอร์ของคุณในขณะที่ตรวจสอบ / ป้องกันการละเมิดในแบบเรียลไทม์ หากไม่มีเซิร์ฟเวอร์สิ่งที่ดีที่สุดที่คุณสามารถทำได้คือตรวจสอบความผิดหลังความจริงเป็นระยะ นี่คือสิ่งที่ฉันจะทำ:

1) หมุนคีย์เป็นระยะสำหรับผู้ใช้ IAM นั้น: ทุกคืนสร้างคีย์ใหม่สำหรับผู้ใช้ IAM นั้นและแทนที่คีย์ที่เก่าที่สุด เนื่องจากมี 2 ปุ่มแต่ละคีย์จะใช้ได้เป็นเวลา 2 วัน

2) เปิดใช้งานการบันทึก S3 และดาวน์โหลดบันทึกทุกชั่วโมง ตั้งค่าการเตือนเมื่อ "อัพโหลดมากเกินไป" และ "ดาวน์โหลดมากเกินไป" คุณจะต้องตรวจสอบทั้งขนาดไฟล์ทั้งหมดและจำนวนไฟล์ที่อัพโหลด และคุณจะต้องการตรวจสอบทั้งผลรวมทั่วโลกและผลรวมต่อที่อยู่ IP (ด้วยเกณฑ์ที่ต่ำกว่า)

การตรวจสอบเหล่านี้สามารถทำได้ "serverless" เพราะคุณสามารถเรียกใช้บนเดสก์ท็อปของคุณ (เช่น S3 ใช้งานได้ทั้งหมดกระบวนการเหล่านี้มีไว้เพื่อเตือนให้คุณใช้ถัง S3 ของคุณในทางที่ผิดดังนั้นคุณจะไม่ได้รับใบเรียกเก็บเงิน AWS ยักษ์เมื่อสิ้นเดือนนี้)


3
ผู้ชายฉันลืมไปว่าสิ่งที่ซับซ้อนมาก่อนแลมบ์ดา
Ryan Shillington

10

การเพิ่มข้อมูลเพิ่มเติมลงในคำตอบที่ยอมรับคุณสามารถอ้างอิงบล็อกของฉันเพื่อดูรหัสที่กำลังรันอยู่โดยใช้ AWS Signature รุ่น 4

จะสรุปที่นี่:

ทันทีที่ผู้ใช้เลือกไฟล์ที่จะอัปโหลดให้ทำดังต่อไปนี้: 1. โทรไปที่เว็บเซิร์ฟเวอร์เพื่อเริ่มบริการเพื่อสร้าง params ที่จำเป็น

  1. ในบริการนี้โทรไปที่บริการ AWS IAM เพื่อรับเครดิตชั่วคราว

  2. เมื่อคุณมีเครดิตแล้วให้สร้างนโยบายฝากข้อมูล (สตริงที่เข้ารหัส 64 ฐาน) จากนั้นลงชื่อนโยบายถังด้วยคีย์การเข้าถึงความลับชั่วคราวเพื่อสร้างลายเซ็นสุดท้าย

  3. ส่งพารามิเตอร์ที่จำเป็นกลับไปที่ UI

  4. เมื่อได้รับสิ่งนี้แล้วให้สร้างวัตถุฟอร์ม html ตั้งค่าพารามิเตอร์ที่จำเป็นและโพสต์มัน

สำหรับข้อมูลรายละเอียดโปรดอ้างอิง https://wordpress1763.wordpress.com/2016/10/03/browser-based-upload-aws-signature-version-4/


5
ฉันใช้เวลาทั้งวันพยายามคิดใน Javascript และคำตอบนี้บอกฉันว่าจะทำอย่างไรโดยใช้ XMLhttprequest ฉันประหลาดใจมากที่คุณถูกโหวต OP ขอ javascript และรับแบบฟอร์มในคำตอบที่แนะนำ ความเศร้าโศกที่ดี ขอบคุณสำหรับคำตอบนี้!
พอลเอส.

BTW superagent มีปัญหา CORS ร้ายแรงดังนั้น xmlhttprequest ดูเหมือนจะเป็นวิธีเดียวที่สมเหตุสมผลในการทำสิ่งนี้ในตอนนี้
Paul S

4

ในการสร้างลายเซ็นฉันต้องใช้รหัสลับของฉัน แต่ทุกสิ่งเกิดขึ้นในฝั่งไคลเอ็นต์ดังนั้นคีย์ลับสามารถเปิดเผยได้อย่างง่ายดายจากแหล่งที่มาของหน้าเว็บ (แม้ว่าฉันจะทำให้งงงวย / เข้ารหัสแหล่งที่มาของฉัน)

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

ลายเซ็นดิจิตอลเช่นที่นี่ใช้เพื่อความปลอดภัยทั่วเว็บ หากใครบางคน (NSA?) สามารถทำลายพวกเขาได้จริงพวกเขาจะมีเป้าหมายที่ใหญ่กว่า S3 bucket ของคุณ :)


2
แต่หุ่นยนต์อาจลองอัปโหลดไฟล์ได้ไม่ จำกัด อย่างรวดเร็ว ฉันจะตั้งค่านโยบายของไฟล์สูงสุดต่อถังได้อย่างไร
Dejell

3

ฉันได้รับรหัสง่ายๆในการอัปโหลดไฟล์จากเบราว์เซอร์ Javascript ไปยัง AWS S3 และแสดงรายการไฟล์ทั้งหมดในที่เก็บ S3

ขั้นตอน:

  1. หากต้องการทราบวิธีการสร้างสร้าง IdentityPoolId http://docs.aws.amazon.com/cognito/latest/developerguide/identity-pools.html

    1. หน้าคอนโซลของ Goto S3 และเปิดการกำหนดค่า cors จากที่เก็บข้อมูลและเขียนโค้ด XML ต่อไปนี้ลงไป

      <?xml version="1.0" encoding="UTF-8"?>
      <CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
       <CORSRule>    
        <AllowedMethod>GET</AllowedMethod>
        <AllowedMethod>PUT</AllowedMethod>
        <AllowedMethod>DELETE</AllowedMethod>
        <AllowedMethod>HEAD</AllowedMethod>
        <AllowedHeader>*</AllowedHeader>
       </CORSRule>
      </CORSConfiguration>
    2. สร้างไฟล์ HTML ที่มีรหัสต่อไปนี้เปลี่ยนข้อมูลรับรองเปิดไฟล์ในเบราว์เซอร์และเพลิดเพลิน

      <script type="text/javascript">
       AWS.config.region = 'ap-north-1'; // Region
       AWS.config.credentials = new AWS.CognitoIdentityCredentials({
       IdentityPoolId: 'ap-north-1:*****-*****',
       });
       var bucket = new AWS.S3({
       params: {
       Bucket: 'MyBucket'
       }
       });
      
       var fileChooser = document.getElementById('file-chooser');
       var button = document.getElementById('upload-button');
       var results = document.getElementById('results');
      
       function upload() {
       var file = fileChooser.files[0];
       console.log(file.name);
      
       if (file) {
       results.innerHTML = '';
       var params = {
       Key: n + '.pdf',
       ContentType: file.type,
       Body: file
       };
       bucket.upload(params, function(err, data) {
       results.innerHTML = err ? 'ERROR!' : 'UPLOADED.';
       });
       } else {
       results.innerHTML = 'Nothing to upload.';
       }    }
      </script>
      <body>
       <input type="file" id="file-chooser" />
       <input type="button" onclick="upload()" value="Upload to S3">
       <div id="results"></div>
      </body>

2
ไม่มีใครสามารถใช้ "IdentityPoolId" ของฉันเพื่ออัปโหลดไฟล์ไปยังที่เก็บข้อมูล S3 ของฉันได้ วิธีนี้เป็นวิธีป้องกันไม่ให้บุคคลที่สามเพียงแค่คัดลอก "IdentityPoolId" ของฉันและอัปโหลดไฟล์จำนวนมากไปยังที่เก็บ S3 ของฉัน
Sahil

1
stackoverflow.com/users/4535741/sahilคุณสามารถป้องกันข้อมูล / ไฟล์ที่อัปโหลดจากโดเมนอื่น ๆ โดยการตั้งค่า CORS ที่เหมาะสมเป็น S3 bucket ดังนั้นแม้ว่าใครก็ตามที่เข้าถึง ID พูลของคุณพวกเขาไม่สามารถจัดการไฟล์ฝากข้อมูล s3 ของคุณได้
Nilesh Pawar

2

หากคุณไม่มีรหัสฝั่งเซิร์ฟเวอร์ความปลอดภัยของคุณขึ้นอยู่กับความปลอดภัยของการเข้าถึงรหัส JavaScript ของคุณในฝั่งไคลเอ็นต์ (เช่นทุกคนที่มีรหัสสามารถอัปโหลดบางสิ่งได้)

ดังนั้นฉันอยากจะแนะนำให้สร้างที่เก็บ S3 พิเศษซึ่งสามารถเขียนได้แบบสาธารณะ (แต่ไม่สามารถอ่านได้) ดังนั้นคุณไม่จำเป็นต้องมีคอมโพเนนต์ที่มีลายเซ็นในฝั่งไคลเอ็นต์

ชื่อฝากข้อมูล (GUID เช่น) จะเป็นการป้องกันการอัปโหลดที่เป็นอันตรายเพียงอย่างเดียวของคุณ (แต่ผู้โจมตีที่มีแนวโน้มไม่สามารถใช้ที่ฝากข้อมูลของคุณเพื่อถ่ายโอนข้อมูลได้เนื่องจากเป็นข้อมูลที่เขียนถึงเขาเท่านั้น)


1

นี่คือวิธีที่คุณสร้างเอกสารนโยบายโดยใช้โหนดและไร้เซิร์ฟเวอร์

"use strict";

const uniqid = require('uniqid');
const crypto = require('crypto');

class Token {

    /**
     * @param {Object} config SSM Parameter store JSON config
     */
    constructor(config) {

        // Ensure some required properties are set in the SSM configuration object
        this.constructor._validateConfig(config);

        this.region = config.region; // AWS region e.g. us-west-2
        this.bucket = config.bucket; // Bucket name only
        this.bucketAcl = config.bucketAcl; // Bucket access policy [private, public-read]
        this.accessKey = config.accessKey; // Access key
        this.secretKey = config.secretKey; // Access key secret

        // Create a really unique videoKey, with folder prefix
        this.key = uniqid() + uniqid.process();

        // The policy requires the date to be this format e.g. 20181109
        const date = new Date().toISOString();
        this.dateString = date.substr(0, 4) + date.substr(5, 2) + date.substr(8, 2);

        // The number of minutes the policy will need to be used by before it expires
        this.policyExpireMinutes = 15;

        // HMAC encryption algorithm used to encrypt everything in the request
        this.encryptionAlgorithm = 'sha256';

        // Client uses encryption algorithm key while making request to S3
        this.clientEncryptionAlgorithm = 'AWS4-HMAC-SHA256';
    }

    /**
     * Returns the parameters that FE will use to directly upload to s3
     *
     * @returns {Object}
     */
    getS3FormParameters() {
        const credentialPath = this._amazonCredentialPath();
        const policy = this._s3UploadPolicy(credentialPath);
        const policyBase64 = new Buffer(JSON.stringify(policy)).toString('base64');
        const signature = this._s3UploadSignature(policyBase64);

        return {
            'key': this.key,
            'acl': this.bucketAcl,
            'success_action_status': '201',
            'policy': policyBase64,
            'endpoint': "https://" + this.bucket + ".s3-accelerate.amazonaws.com",
            'x-amz-algorithm': this.clientEncryptionAlgorithm,
            'x-amz-credential': credentialPath,
            'x-amz-date': this.dateString + 'T000000Z',
            'x-amz-signature': signature
        }
    }

    /**
     * Ensure all required properties are set in SSM Parameter Store Config
     *
     * @param {Object} config
     * @private
     */
    static _validateConfig(config) {
        if (!config.hasOwnProperty('bucket')) {
            throw "'bucket' is required in SSM Parameter Store Config";
        }
        if (!config.hasOwnProperty('region')) {
            throw "'region' is required in SSM Parameter Store Config";
        }
        if (!config.hasOwnProperty('accessKey')) {
            throw "'accessKey' is required in SSM Parameter Store Config";
        }
        if (!config.hasOwnProperty('secretKey')) {
            throw "'secretKey' is required in SSM Parameter Store Config";
        }
    }

    /**
     * Create a special string called a credentials path used in constructing an upload policy
     *
     * @returns {String}
     * @private
     */
    _amazonCredentialPath() {
        return this.accessKey + '/' + this.dateString + '/' + this.region + '/s3/aws4_request';
    }

    /**
     * Create an upload policy
     *
     * @param {String} credentialPath
     *
     * @returns {{expiration: string, conditions: *[]}}
     * @private
     */
    _s3UploadPolicy(credentialPath) {
        return {
            expiration: this._getPolicyExpirationISODate(),
            conditions: [
                {bucket: this.bucket},
                {key: this.key},
                {acl: this.bucketAcl},
                {success_action_status: "201"},
                {'x-amz-algorithm': 'AWS4-HMAC-SHA256'},
                {'x-amz-credential': credentialPath},
                {'x-amz-date': this.dateString + 'T000000Z'}
            ],
        }
    }

    /**
     * ISO formatted date string of when the policy will expire
     *
     * @returns {String}
     * @private
     */
    _getPolicyExpirationISODate() {
        return new Date((new Date).getTime() + (this.policyExpireMinutes * 60 * 1000)).toISOString();
    }

    /**
     * HMAC encode a string by a given key
     *
     * @param {String} key
     * @param {String} string
     *
     * @returns {String}
     * @private
     */
    _encryptHmac(key, string) {
        const hmac = crypto.createHmac(
            this.encryptionAlgorithm, key
        );
        hmac.end(string);

        return hmac.read();
    }

    /**
     * Create an upload signature from provided params
     * https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-authenticating-requests.html#signing-request-intro
     *
     * @param policyBase64
     *
     * @returns {String}
     * @private
     */
    _s3UploadSignature(policyBase64) {
        const dateKey = this._encryptHmac('AWS4' + this.secretKey, this.dateString);
        const dateRegionKey = this._encryptHmac(dateKey, this.region);
        const dateRegionServiceKey = this._encryptHmac(dateRegionKey, 's3');
        const signingKey = this._encryptHmac(dateRegionServiceKey, 'aws4_request');

        return this._encryptHmac(signingKey, policyBase64).toString('hex');
    }
}

module.exports = Token;

วัตถุการกำหนดค่าที่ใช้จะถูกเก็บไว้ในที่เก็บพารามิเตอร์ SSM และมีลักษณะเช่นนี้

{
    "bucket": "my-bucket-name",
    "region": "us-west-2",
    "bucketAcl": "private",
    "accessKey": "MY_ACCESS_KEY",
    "secretKey": "MY_SECRET_ACCESS_KEY",
}

0

หากคุณยินดีที่จะใช้บริการบุคคลที่สาม auth0.com รองรับการรวมนี้ บริการ auth0 แลกเปลี่ยนการรับรองความถูกต้องบริการบุคคลที่สาม SSO สำหรับโทเค็นเซสชัน AWS ชั่วคราวจะ จำกัด สิทธิ์

ดู: https://github.com/auth0-samples/auth0-s3-sample/
และเอกสาร auth0


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