DEV Community

Walid Elnozahy
Walid Elnozahy

Posted on

Secure your backend with aws

Secure your backend with aws

While I was working on a react project recently and had to use an API to fetch data for the application I faced an issue while I was trying to deploy the app on Netlify for testing and feedback, the API endpoint was not secured (HTTPS) which caused security issues. I wanted an easy solution to be able to easily secure any endpoint so I created a small CLI tool to solve this.

In this blog post, I will explain how I wrote the CLI tool and show you how to simply use aws gateway to secure your endpoints.

Usage

You need to create an aws account (if you don’t have one already)

npm install --save @walidelnozahy/secure
Enter fullscreen mode Exit fullscreen mode

then simply just call secure http://example.com in your terminal

secure http://example.com

// https://id.execute-api.us-west-1.amazonaws.com/dev
Enter fullscreen mode Exit fullscreen mode

How it works

Steps needed to secure the endpoint with API gateway are:

1. Create a new rest API.
2. Get resources for the created rest API (to get parent id).
3. Create resource (with parent id).
4. Put method with created resource id and request params.
5. Put method with parent id.
6. Put integration for the resource id.
7. Put integration for the parent id.
8. Create a deployment.
9. Create stage.
Enter fullscreen mode Exit fullscreen mode

so let’s get started. first, we will need aws SDK
Install aws-sdk by :

npm install --save aws-sdk
Enter fullscreen mode Exit fullscreen mode

then we’ll create a file called secure.js.

we will require aws and update the region in the config for the desired region in this case we will use us-west-1 , then create a function called secureMyBackend that will initiate everything

// secure.js
const aws = require('aws-sdk')
aws.config.update({ region: 'us-west-1' })

const secureMyBackend = async () => {}
Enter fullscreen mode Exit fullscreen mode

Creating the Rest API

first, we will need to create a rest API and give it a name and endpointConfiguration with types of ['REGIONAL'] and then get it’s id because we will need it later, and then we will get resources for rest api that we just created in order to get the parentId.

// secure.js
const aws = require('aws-sdk')
aws.config.update({ region: 'us-west-1' })

const createRestApi = async (name) => {
  const res = await apigateway
    .createRestApi({
      name,
      endpointConfiguration: {
        types: ['REGIONAL']
      }
    })
    .promise()
  return res.id
}

const secureMyBackend = async () => {
  const restApiId = await createRestApi(`new api`)
 const resources = await apigateway
    .getResources({
      restApiId
    })
    .promise()
const parentId = resources.items[0].id
}
Enter fullscreen mode Exit fullscreen mode

Creating Resources & Methods

then we need to create resources and methods.

We will create the resources using createResource and pass parentId , restApId and the pathPart which will take a value of {proxy+}

// secure.js
const aws = require('aws-sdk')
aws.config.update({ region: 'us-west-1' })

const createRestApi = async (name) => {
  const res = await apigateway
    .createRestApi({
      name,
      endpointConfiguration: {
        types: ['REGIONAL']
      }
    })
    .promise()
  return res.id
}

const createResource = async ({ restApiId, parentId }) => {
  const res = await apigateway
    .createResource({
      parentId,
      pathPart: '{proxy+}',
      restApiId
    })
    .promise()
  return res.id
}


const secureMyBackend = async () => {
  const restApiId = await createRestApi(`new api`)
 const resources = await apigateway
    .getResources({
      restApiId
    })
    .promise()
const parentId = resources.items[0].id
const resourceId = await createResource({ restApiId, parentId })
}
Enter fullscreen mode Exit fullscreen mode

then we need to put two methods, one for the resource we just created and pass requestParameters with value { 'integration.request.path.proxy': 'method.request.path.proxy'} and another put method for the resource created with restApiId and pass parentId

// secure.js
const aws = require('aws-sdk')
aws.config.update({ region: 'us-west-1' })

const createRestApi = async (name) => {
const res = await apigateway
.createRestApi({
name,
endpointConfiguration: {
types: ['REGIONAL']
}
})
.promise()
return res.id
}

const createResource = async ({ restApiId, parentId }) => {
const res = await apigateway
.createResource({
parentId,
pathPart: '{proxy+}',
restApiId
})
.promise()
return res.id
}

const putMethod = async ({ resourceId, restApiId, requestParameters }) => {
const res = await apigateway
.putMethod({
...({ requestParameters } || {}),
authorizationType: 'NONE',
httpMethod: 'ANY',
resourceId,
restApiId
})
.promise()
return res
}

const secureMyBackend = async () => {
const restApiId = await createRestApi(new api)
const resources = await apigateway
.getResources({
restApiId
})
.promise()
const parentId = resources.items[0].id
const resourceId = await createResource({ restApiId, parentId })
await putMethod({
restApiId,
resourceId,
requestParameters: { 'method.request.path.proxy': true }
})
await putMethod({ restApiId, resourceId: parentId })
}

Enter fullscreen mode Exit fullscreen mode




Creating the Proxy Integration

we need to put 2 integrations one for resourceId and pass requestParameters as 'integration.request.path.proxy': 'method.request.path.proxy' and pass uri: 'url/{proxy}' and one for parentId and pass the uri

// secure.js
const aws = require('aws-sdk')
aws.config.update({ region: 'us-west-1' })

const createRestApi = async (name) => {
const res = await apigateway
.createRestApi({
name,
endpointConfiguration: {
types: ['REGIONAL']
}
})
.promise()
return res.id
}

const createResource = async ({ restApiId, parentId }) => {
const res = await apigateway
.createResource({
parentId,
pathPart: '{proxy+}',
restApiId
})
.promise()
return res.id
}

const putMethod = async ({ resourceId, restApiId, requestParameters }) => {
const res = await apigateway
.putMethod({
...({ requestParameters } || {}),
authorizationType: 'NONE',
httpMethod: 'ANY',
resourceId,
restApiId
})
.promise()
return res
}

const putIntegration = async (props) => {
const { resourceId, restApiId, uri, requestParameters } = props
const params = {
httpMethod: 'ANY',
integrationHttpMethod: 'ANY',
type: 'HTTP_PROXY',
resourceId,
restApiId,
uri,
...({ requestParameters } || {}),
passthroughBehavior: 'WHEN_NO_MATCH'
}
const res = await apigateway.putIntegration(params).promise()
return res
}

const secureMyBackend = async () => {
const restApiId = await createRestApi(new api)
const resources = await apigateway
.getResources({
restApiId
})
.promise()
const parentId = resources.items[0].id
const resourceId = await createResource({ restApiId, parentId })
await putMethod({
restApiId,
resourceId,
requestParameters: { 'method.request.path.proxy': true }
})
await putMethod({ restApiId, resourceId: parentId })
await putIntegration({
restApiId,
resourceId,
uri: ${url}/{proxy},
requestParameters: {
'integration.request.path.proxy': 'method.request.path.proxy'
}
})
await putIntegration({
restApiId,
resourceId: parentId,
uri: url
})
}

Enter fullscreen mode Exit fullscreen mode




Deploying the API

and finally, we need to create a deployment and create a stage with deploymentId and then return the new secured endpoint

// secure.js
const aws = require('aws-sdk')
aws.config.update({ region: 'us-west-1' })

const createRestApi = async (name) => {
const res = await apigateway
.createRestApi({
name,
endpointConfiguration: {
types: ['REGIONAL']
}
})
.promise()
return res.id
}

const createResource = async ({ restApiId, parentId }) => {
const res = await apigateway
.createResource({
parentId,
pathPart: '{proxy+}',
restApiId
})
.promise()
return res.id
}

const putMethod = async ({ resourceId, restApiId, requestParameters }) => {
const res = await apigateway
.putMethod({
...({ requestParameters } || {}),
authorizationType: 'NONE',
httpMethod: 'ANY',
resourceId,
restApiId
})
.promise()
return res
}

const putIntegration = async (props) => {
const { resourceId, restApiId, uri, requestParameters } = props
const params = {
httpMethod: 'ANY',
integrationHttpMethod: 'ANY',
type: 'HTTP_PROXY',
resourceId,
restApiId,
uri,
...({ requestParameters } || {}),
passthroughBehavior: 'WHEN_NO_MATCH'
}
const res = await apigateway.putIntegration(params).promise()
return res
}

const secureMyBackend = async () => {
const restApiId = await createRestApi(new api)
const resources = await apigateway
.getResources({
restApiId
})
.promise()
const parentId = resources.items[0].id
const resourceId = await createResource({ restApiId, parentId })
await putMethod({
restApiId,
resourceId,
requestParameters: { 'method.request.path.proxy': true }
})
await putMethod({ restApiId, resourceId: parentId })
await putIntegration({
restApiId,
resourceId,
uri: ${url}/{proxy},
requestParameters: {
'integration.request.path.proxy': 'method.request.path.proxy'
}
})
await putIntegration({
restApiId,
resourceId: parentId,
uri: url
})
const deploymentId = await createDeployment({ restApiId })
await createStage({ restApiId, deploymentId })
return secured url: https://${restApiId}.execute-api.us-west-1.amazonaws.com/dev
}

Enter fullscreen mode Exit fullscreen mode




Conclusion

I hope this short tutorial was helpful. you can find repo of the above example on Github at https://github.com/wellyelnozahy/secure-my-backend

you can find the CLI tool here https://www.npmjs.com/package/@walidelnozahy/secure

Top comments (0)