Skip to content

Instantly share code, notes, and snippets.

@aurelijusb
Last active September 8, 2019 15:45
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save aurelijusb/87ceabc1fa980dd27278063ea49d1e6e to your computer and use it in GitHub Desktop.
Save aurelijusb/87ceabc1fa980dd27278063ea49d1e6e to your computer and use it in GitHub Desktop.
Material used for demo of: How AWS handles security @ VilniusPHP 0x52

Example to upload to S3

This example shows, how to use AWS JavaScript (Browser) SDK to upload files from Frontend to S3 bucket (as opposite to traditional PHP bancked handling all uploaded files)

Usage

  • Have an AWS Account
  • (Prefered) Create user with AdministratorAccess managed policy (or less). Then replace arn:aws:iam::${AWS::AccountId}:root with your user ARN (cat be fetched by aws sts get-caller-identity or by from AWS Web interface)
  • Install and configure AWS CLI
  • Create AWS Cloud Formation stack from s3uploader-cf.yml template.
  • Use Ouptut (there is a tab in AWS Web UI) to get bucket name and example to use for aws sts assume-role
  • Update index.html to use your created bucket and AWS.Credentials
  • Open file index.html via browser and try to upload something (errors are logged into Broser's Console)
  • To be confident, files should be stored to AWS S3 bucket in your account (name of the bucket is autogenerated)

References

<!DOCTYPE html>
<html><head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>VilniusPHP 0x52: Upload to S3 Example</title>
<script src="https://sdk.amazonaws.com/js/aws-sdk-2.523.0.min.js"></script>
</head>
<body>
<div>Current user: <span id="user-arn"></span></div>
<div><input type="file" id="file-chooser">
<button id="upload-button">Upload</button>
</div>
<div>Uploaded:
<div id="content"></div>
</div>
<script type="text/javascript">
// Utility functions
const display = (id, value) => {
document.getElementById(id).innerHTML = value
};
// We need initial user (Principal) to act further
AWS.config.update({
region: 'eu-west-1',
credentials: new AWS.Credentials({ // Change to your own using aws sts assume-role
accessKeyId: 'UPDATE_HERE',
secretAccessKey: 'UPDATE_HERE',
sessionToken: 'UPDATE_HERE'
})
});
// Configuration
AWS.config.setPromisesDependency(Promise);
let sts = new AWS.STS();
var bucket = new AWS.S3({
params: {
Bucket: "UPDATE_HERE" // Change to your S3 bucket (AWS Role/Policy should allow to write to this bucket)
}
});
var prefix = "demo-session";
const displayContent = () => {
bucket.listObjects({Prefix: prefix}).promise().then((data) => {
let objKeys = '';
data.Contents.forEach(function (obj) {
objKeys += obj.Key + "<br>";
});
display('content', objKeys)
}).catch((err, data) => console.log(err, err.stack, data));
};
// Check, who we are
sts.getCallerIdentity({}).promise()
.then((data) => {
console.log('Caller identity', data);
display("user-arn", data.Arn);
displayContent();
})
.catch((err, data) => console.log(err, err.stack, data));
// For file upload
let button = document.getElementById('upload-button');
button.addEventListener('click', () => {
let file = document.getElementById('file-chooser').files[0];
console.log('file', file);
bucket.putObject({
Key: prefix + "/" + file.name,
ContentType: file.type,
Body: file,
ACL: 'public-read'
}).promise().then((data) => {
console.log("Sent", data);
displayContent();
}).catch((err, data) => console.log(err, err.stack, data));
});
displayContent();
</script>
<pre>
</pre>
</body></html>
Description: |
Example to upload to S3 from Frontend (HTML) application
Used as a demo during talk "How AWS handles security" at VilniusPHP 0x52
Resources:
FileStorage: # During example used "vilniusphp-0x52-s3-uploader" name. Now it will be autogenerated (for reproducability)
Type: AWS::S3::Bucket # For simplicity not including Public access (it is too easy to mess up things, when they are publicly available). You could check your application by going to S3 bucket via AWS Web interface.
Properties:
CorsConfiguration:
CorsRules: # JavaScript SDK needs CORS to access S3. For simplicity allowing everything. Best practice is to restrict to specific domain/methods/headers
- AllowedHeaders:
- "*"
AllowedMethods:
- "GET"
- "PUT"
- "POST"
- "DELETE"
AllowedOrigins:
- "*"
MaxAge: "3000"
S3Uploader: # During example used "vilniusphp-0x52-s3-uploader" name. Now it will be autogenerated (for reproducability)
Type: "AWS::IAM::Role"
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: "Allow"
Principal:
AWS:
# Replace with the "Arn" value of "aws sts get-caller-identity" (E.g. "arn:aws:iam::327920853133:user/aurelijus-local-tests")
Fn::Sub: "arn:aws:iam::${AWS::AccountId}:root" # Using "root" user for simplicity.
Action:
- "sts:AssumeRole"
Path: "/"
Policies:
- PolicyName: "s3-upload"
PolicyDocument: # During demo we used JSON, but CloudFormation template is easier to read in YAML format
Version: '2012-10-17'
Statement:
- Action: # Needed to allow uploading to S3
- s3:PutObject
- s3:PutObjectAcl
Resource:
- Fn::Sub: "arn:aws:s3:::${FileStorage}/demo-session/*" # Replacing hardoced vaue with the name of generated S3 bucket
Effect: Allow
- Effect: Allow # Needed to show list of the S3 bucket (folder) content
Action:
- s3:Get*
- s3:List*
Resource:
- Fn::Sub: "arn:aws:s3:::${FileStorage}" # Replacing hardoced vaue with the name of generated S3 bucket
- Fn::Sub: arn:aws:s3:::${FileStorage}/* # Replacing hardoced vaue with the name of generated S3 bucket
Outputs:
Bucket:
Description: Bucket name, that will store uploaded files. This name should be used for "Bucket" in "index.html" file
Value:
Ref: FileStorage
RoleToAssume:
Description: Command line to retrieve temporary credentials. Those should be used for "AWS.config.update" in "index.html" file
Value:
Fn::Sub:
- 'aws sts assume-role --role-arn "${S3UploaderARN}" --role-session-name "vilniusphp-test"'
- S3UploaderARN:
Fn::GetAtt:
- S3Uploader
- "Arn"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment