使用授权签名Ver 4从浏览器直接上传到S3

时间:2015-10-16 13:19:54

标签: file-upload amazon-s3 http-post

我需要直接从浏览器上传文件到S3。一开始我创建了一个正在运行的脚本但是要授权我需要将我的凭证设置为accessKeyId和secretAccessKey,这是不安全的。

我发现我可以使用“Authorization Signature

进行授权

看起来很棒,但我找不到可以将此授权标头放在upload() method中的请求的位置。

我的授权标题的一个示例:

  

授权:A​​WS4-HMAC-SHA256   凭证= / 20151016 // S3 / aws4_request,   SignedHeaders =内容类型;主机; X-AMZ-日期,   签名= 4eee344a71a58623febc4079024a27cb62f3d26546695422244fcefe50d0168d

感谢您的建议。

2 个答案:

答案 0 :(得分:1)

我找到了解决这个问题的方法。我的解决方案基于此site的示例。

在最终解决方案中,我不使用javascript SDK,它使用带有授权输入的帖子表单,以及使用帖子参数发送的内容。

答案 1 :(得分:0)

您可以使用您的POST请求附上已签名的policy document,以便使用AWS签名版本4安全地进行身份验证。

如果您使用的是Node,则可以使用服务器上的aws-s3-form软件包生成客户端所需的表单数据,以便向S3发送成功的请求。

您可能希望阅读有关该主题的blog post以获得完整的洞察力。

示例服务器端代码(节点)

let AwsS3Form = require('aws-s3-form')

[...]

// A hapi.js server route
server.route({
  method: ['GET',],
  path: '/api/s3Settings',
  config: {
    auth: 'session',
    handler: (request, reply) => {
      let {key,} = request.query

      let keyPrefix = `u/${request.auth.credentials.username}/`
      let region = process.env.S3_REGION
      let s3Form = new AwsS3Form({
        accessKeyId: process.env.AWS_ACCESS_KEY,
        secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
        region,
        bucket,
        keyPrefix,
        successActionStatus: 200,
      })
      let url = `https://s3.${region}.amazonaws.com/${bucket}/${keyPrefix}${key}`
      let formData = s3Form.create(key)
      reply({
        bucket,
        region,
        url,
        fields: formData.fields,
      })
    },
  },
})

示例客户端代码

let R = require('ramda')

let ajax = require('./ajax')

class S3Uploader {
  constructor({folder,}) {
    this.folder = folder
  }

  send(file) {
    let key = `${this.folder}/${file.name}`
    return ajax.getJson(`s3Settings`, {key,})
      .then((s3Settings) => {
        let formData = new FormData()
        R.forEach(([key, value,]) => {
          formData.append(key, value)
        }, R.toPairs(s3Settings.fields))
        formData.append('file', file)

        return new Promise((resolve, reject) => {
          let request = new XMLHttpRequest()
          request.onreadystatechange = () => {
            if (request.readyState === XMLHttpRequest.DONE) {
              if (request.status === 200) {
                resolve(s3Settings.url)
              } else {
                reject(request.responseText)
              }
            }
          }

          let url = `https://s3.${s3Settings.region}.amazonaws.com/${s3Settings.bucket}`
          request.open('POST', url, true)
          request.send(formData)
        })
      }, (error) => {
        throw new Error(`Failed to receive S3 settings from server`)
      })
  }
}