Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
101 commits
Select commit Hold shift + click to select a range
c4c407f
Update run script to not remove all Docker containers from host
irvinlim Feb 25, 2018
374ed91
Update Dockerfile and entrypoint scripts to use Node.js
irvinlim Feb 26, 2018
e738973
Add base files for API and app server
irvinlim Feb 26, 2018
9bf95b2
Fix port number for Express server
irvinlim Feb 26, 2018
6decaf8
Speed up Docker builds by ignoring Node build files in build context
irvinlim Feb 26, 2018
9a298f4
Fix starting of services
irvinlim Feb 26, 2018
01ab3ac
Add editor configs for EC and prettier
irvinlim Feb 26, 2018
df741b7
Add nodemon for hot reloading of Express
irvinlim Feb 26, 2018
b2d3380
Remove unneeded Express boilerplate files and create index route
irvinlim Feb 26, 2018
69bebfd
Add result wrapper middleware
irvinlim Feb 26, 2018
1a97b0d
Wrap errors according to specs also
irvinlim Feb 26, 2018
275c480
Handle cases when result is null or undefined
irvinlim Feb 26, 2018
f3cc23b
Refactor router nesting and change approach to list endpoints
irvinlim Feb 26, 2018
c50016a
Add members route
irvinlim Feb 26, 2018
febcd3e
Add router and base components for Vue app
irvinlim Feb 26, 2018
745296e
Implement simple REST client fetching with async/await
irvinlim Feb 26, 2018
cab010d
Handle errors with non-error status codes properly
irvinlim Feb 26, 2018
d0bec62
Add getStatus method to the REST client and fetch heartbeat
irvinlim Feb 26, 2018
7544dd1
Update navbar with actual routes
irvinlim Feb 26, 2018
b8761a7
Add Vuex and cookies capabilities for user session management
irvinlim Feb 26, 2018
af3f415
Remove custom middleware because there is no standard output format...
irvinlim Feb 26, 2018
593d771
Add stub methods for /users/* endpoints with validation
irvinlim Feb 26, 2018
af05756
Utilise Vuex state to conditionally show login/logout buttons
irvinlim Feb 26, 2018
9347da1
Add login form and handle user session management
irvinlim Feb 26, 2018
83a7048
Refactor Vuex types and redirect from login page if already logged in
irvinlim Feb 26, 2018
26fe2f9
Fix fixed bottom footer positioning CSS
irvinlim Feb 26, 2018
805ecb4
Add register route
irvinlim Feb 26, 2018
8a87081
Add stub methods for /diary endpoints
irvinlim Feb 26, 2018
79d461e
Add public diary entries view
irvinlim Feb 26, 2018
d5f8f97
Add error notification for PublicDiary
irvinlim Feb 26, 2018
adef951
Add personal diary view
irvinlim Feb 26, 2018
0daeb54
Add new diary entry view
irvinlim Feb 26, 2018
86d36f2
Add success notification when redirecting back to personal diary
irvinlim Feb 26, 2018
85eba01
Remove irrelevant volume mount
irvinlim Feb 26, 2018
4ce8b7d
Remove reference src files
irvinlim Feb 26, 2018
50ff7dc
Remove redundant packages
irvinlim Feb 26, 2018
ffd239d
Update README
irvinlim Feb 26, 2018
4800927
Remove unused import
yong24s Feb 27, 2018
8a1cfc3
Remove vue-toasted usage in app
irvinlim Feb 27, 2018
0428cbc
Add Docker Compose file and extend scripts to opt in
irvinlim Feb 27, 2018
1b490f1
Change run script to use Docker Compose instead
irvinlim Feb 27, 2018
b2ca762
Use team ID as Docker Compose project name
irvinlim Feb 27, 2018
e1b2800
Remove unused service startup scripts
irvinlim Feb 27, 2018
3b602e3
Add app NPM wait script to ensure API is up before attempting to proxy
irvinlim Feb 27, 2018
f9b6dce
Add waitdb script before starting Express server
irvinlim Feb 27, 2018
6d593a1
Remove unnecessary Dockerfile
irvinlim Feb 27, 2018
56822b6
Revert to using result wrapper middleware due to assignment spec modi…
irvinlim Feb 27, 2018
0e8065c
Change default error status code to 200
irvinlim Feb 27, 2018
4932b12
Modify UI to automatically extract from .result
irvinlim Feb 27, 2018
d8b5584
Update users endpoints to follow spec
irvinlim Feb 27, 2018
94254ed
Refactor resultWrapper middleware
irvinlim Feb 27, 2018
bb7c1b0
Add user profile display on home page
irvinlim Feb 27, 2018
355fe1b
Add mongo-express in docker compose
yong24s Feb 28, 2018
4c8544c
Use mongoose and implement user apis fully
yong24s Feb 28, 2018
c3277ec
Stage files containing user apis logic
yong24s Feb 28, 2018
800ddae
Add diary api logic
yong24s Feb 28, 2018
38065f9
Add missing model dependancies
yong24s Mar 1, 2018
4396531
Fix logic error in diary create api and add a pseudo auto increment f…
yong24s Mar 1, 2018
d361818
Merge branch 'add-sql'
yong24s Mar 1, 2018
5f5c2d7
Merge conflicts
yong24s Mar 1, 2018
4f62597
Use babel-watch with async/await for API and fix up all return values
irvinlim Mar 3, 2018
3eb2169
Add auto increment for ID and remove _id fields from being returned
irvinlim Mar 3, 2018
1a9df97
Update README
irvinlim Mar 3, 2018
2cf6a8c
Use Joi (with celebrate middleware) to validate input
irvinlim Mar 3, 2018
1d28bed
Update README
irvinlim Mar 3, 2018
2e71acf
Add more sections in README
irvinlim Mar 3, 2018
0cef1ca
Update README.md
alwinsonauyong Mar 3, 2018
9d7a224
Change password hashing method to bcrypt
yong24s Mar 3, 2018
6b00663
Merge pull request #22 from CS5331-1718-G3/sanitize-token
irvinlim Mar 3, 2018
622394f
Merge branch 'master' into change_passwordhashingmethod
yong24s Mar 3, 2018
dd9d762
Clean up compose file and extract mongo credentials
irvinlim Mar 3, 2018
d5441a1
Merge branch 'master' of github.com:CS5331-1718-G3/rest-api-development
irvinlim Mar 3, 2018
04cf4d9
Merge pull request #24 from CS5331-1718-G3/change_passwordhashingmethod
irvinlim Mar 3, 2018
9f2d2a8
Update package-lock.json
irvinlim Mar 3, 2018
e7b986a
Use aggregate instead of find in order to map entries in a pipeline
irvinlim Mar 3, 2018
bd59933
Merge pull request #25 from CS5331-1718-G3/date-format
irvinlim Mar 3, 2018
3d066e4
Reload personal diary entries after modifying them
irvinlim Mar 3, 2018
04cb0ce
Merge pull request #26 from CS5331-1718-G3/reload-diary-ui
irvinlim Mar 3, 2018
7c982a4
Modify waitdb for mongo
irvinlim Mar 3, 2018
76d3e4f
Merge pull request #28 from CS5331-1718-G3/waitdb
irvinlim Mar 3, 2018
6373e9d
Remove http-server dependency
irvinlim Mar 3, 2018
cb481c2
Add restart policies and remove mongo-express from Docker Compose
irvinlim Mar 3, 2018
53f2999
Update README
irvinlim Mar 3, 2018
e4f9a91
Prevent hardcoded cookie domain
irvinlim Mar 3, 2018
e0e54fb
Update README
irvinlim Mar 3, 2018
4b4aa1d
Update README
irvinlim Mar 3, 2018
0c50a33
Update README
irvinlim Mar 3, 2018
dab3f7c
Add scripts to automate testing
yong24s Mar 4, 2018
810ea5b
Add tests for users endpoint
yong24s Mar 4, 2018
0b7d1a3
Change running instructions
yong24s Mar 4, 2018
fd292e2
Add some tests for diary endpoint.
yong24s Mar 4, 2018
dbbd977
Add user-defined testing methods into tavern
yong24s Mar 4, 2018
3247458
Fix REST error return format
irvinlim Mar 4, 2018
44b5d3a
Add more tests
yong24s Mar 5, 2018
8e426a4
Merge remote-tracking branch 'origin/master' into automate_testing
yong24s Mar 5, 2018
64549af
Update tests to match api changes
yong24s Mar 5, 2018
663f659
Merge pull request #29 from CS5331-1718-G3/automate_testing
yong24s Mar 5, 2018
5be2757
Update README
irvinlim Mar 5, 2018
0b2cfc9
Merge branch 'master' of github.com:CS5331-1718-G3/rest-api-development
irvinlim Mar 5, 2018
474d1cf
Update README.md
yong24s Mar 5, 2018
883f8cf
Update README.md
alwinsonauyong Mar 5, 2018
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"useTabs": false,
"tabWidth": 2
}
14 changes: 0 additions & 14 deletions Dockerfile

This file was deleted.

199 changes: 107 additions & 92 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,143 +1,158 @@
# rest-api-development
# CS5331 Assignment 1 Project: REST API Development

CS5331 Assignment 1 Project Reference Repository
CS5331 Assignment 1 Project

## Instructions
## Team Members

Your objective is to implement a web application that provides the endpoints
specified here: https://cs5331-assignments.github.io/rest-api-development/.
1. Au-yong Xiang Rong Alwinson
2. Irvin Lim Wei Quan
3. Tan Ngee Joel Jonas
4. Teng Yong Hao

The project has been packaged in an easy to set-up docker container with the
skeleton code implemented in Python Flask. You are not restricted in terms of
which language, web stack, or database you desire to use. However, please note
that very limited support can be given to those who decide to veer off the
beaten path.
## Screenshots

You may be required to modify the following files/directories:
![Home Page](./img/screen1.png)
![Login Page](./img/screen2.png)
![Diary Entries Page](./img/screen3.png)
![New Diary Entry Page](./img/screen4.png)

- Dockerfile - contains the environment setup scripts to ensure a homogenous
development environment
- src/ - contains the front-end code in `html` and the skeleton Flask API code
in `service`
- img/ - contains images used for this README
## Short Answer Questions

Assuming you're developing on an Ubuntu 16.04 machine, the quick instructions
to get up and running are:
### Question 1: Briefly describe the web technology stack used in your implementation.

```
# Install Docker
#### Frontend

sudo apt-get update
sudo apt-get install \
apt-transport-https \
ca-certificates \
curl \
software-properties-common
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
sudo add-apt-repository \
"deb [arch=amd64] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) \
stable"
sudo apt-get update
sudo apt-get install docker-ce
* [_Vue.js_](https://vuejs.org): JavaScript view library for fast prototyping and performance
* [_SCSS_](https://sass-lang.com/): CSS preprocessor
* [_Webpack_](https://webpack.js.org/): Module bundler and development web server
* `webpack-dev-server` allows proxying specific routes to a different address, which is used for proxying the API server
* [_Babel_](https://babeljs.io/): JavaScript transpiler

# Verify Docker Works
#### REST API

sudo docker run hello-world
* [_Express_](https://expressjs.com/): Web framework popular for REST APIs in Node.js
* [_MongoDB_](https://www.mongodb.com/): Document store-based database server

# Run the skeleton implementation
#### Development and deployment

sudo ./run.sh
```
* [_Docker_](https://www.docker.com/): Containerization platform for reproducible and quick builds

(Docker CE installation instructions are from this
[link](https://docs.docker.com/install/linux/docker-ce/ubuntu/#install-using-the-repository).)
### Question 2: Are there any security considerations your team thought about?

**Please consult your assignment hand-out for detailed setup information.**
#### Password security

## Grading
In order to prevent rainbow table attacks, we used a random salt for each password hash, using the popular implementation `bcrypt`. This method generates a random 128-bit salt each time a hash is generated, appended to the original password, followed by a generation of a 184-bit hash digest using the Blowfish cipher.

The implementation will be graded in an automated fashion on an Ubuntu 16.04
virtual machine by building the docker container found in your repository and
running it. The grading script will interact with your API.
#### Authorization and policy checks

The following ports are expected to be accessible:
Additionally, to prevent authorization vulnerabilities, we added additional verification such that users can only modify their own diary entries, even though this was not in the specifications.

1. 80, on which static HTML content, including the front-end, is served.
2. 8080, on which the API is exposed.
#### Injection vulnerabilities

To verify this, please run the following commands:
Since we are using MongoDB, the traditional SQL injection vulnerabilities are no longer applicable. However, a lesser-known class of injection vulnerabilities do apply to NoSQL databases such as MongoDB, known as NoSQL injections. This can occur if we pass a JSON object instead of a string, which can result in overwriting the `$where` condition, much like how SQL injections work.

```
sudo ./run.sh
In order to prevent such injection attacks, we perform input sanitization at the API level, which performs type-checking and type-casting using the JavaScript [joi](https://github.com/hapijs/joi) library. Hence, passing this will not work, since `token` is expected to be a string:

```json
{
"token": {
"$nin": ["A"]
}
}
```

On a different window:
Additionally, since strings are not evaluated, arbitrary objects or JavaScript will not be evaluated, which could possibly allow for more complex queries in MongoDB such as:

```
curl http://localhost:80
curl http://localhost:8080
```json
{ "token": "{\"$nin\": [\"A\"]}" }
```

If a response is received, you're good to go.
#### Cross-site scripting

**Please replace the details below with information relevant to your team.**
To prevent XSS, using a UI library such as Vue.js is helpful, since all UI elements are rendered on the client-side through JavaScript. Though it might be more inefficient, this new paradigm of developing frontend applications also helps to prevent XSS for the most part. Any strings that are to be rendered within the HTML are always escaped using the relevant HTML entities.

## Screenshots
By ensuring a single source of truth of data from the API server, this prevents potential double-unescaping bugs which may result in HTML elements being rendered on the browser.

This prevents both reflected and persistent XSS, since we are escaping untrusted input when it is being displayed as HTML.

#### Cross-Origin Resource Sharing (CORS)

Rather than enabling CORS by setting the `Access-Control-Allow-Origin` header so that cross-origin `XMLHttpRequest`s can be made successfully, we proxied any requests to the app server (i.e. `localhost:80`) on the `/api` route to the API server (i.e. `localhost:8080`). This prevents arbitrary access of the API from other origins, such as a malicious site.

More details are explained under [Question 4: Proxying of the API Server](#proxying-of-api-server).

#### Cookie domains

The cookies that are saved on the client are restricted to the same domain as the actual site. This means that any requests that the browser makes that are made outside of the site's domain, will not have the cookies sent along with it. This prevents session hijacking attacks, such as through XSS (if possible), or if an externally-hosted JavaScript file that is embedded in the website is compromised.

#### Session ID prediction

To prevent the possibility of session ID prediction, in which an attacker can sucessfully predicts a user's session ID, our session ID are generated using a cryptographically secure pseudo-random number generator (PRNG) by using UUID version 4, which eliminates the possibility of an attacker being able to predict a user session ID, and hence spoof the identity of another logged in user.

### Question 3: Are there any improvements you would make to the API specification to improve the security of the web application?

#### Authorization checks

As previously mentioned, we believe that the policy for CRUD actions on users' diary entries should be explicitly specified. We had inferred that a user should not be able to modify other user's entries, which was not actually specified in the API specification.

#### Password complexity

Please replace the example screenshots with screenshots of your completed
project. Feel free to include more than one.
There should be some explicit limits for the password length. For example, passwords should be of a considerable length in order to significantly lower the chances of successful brute-force attacks. Since the search space exhibits polynomial growth with respect to the length of the password, by enforcing all passwords to be at least 8 characters (for example) would prevent brute-force attacks on passwords that are too short and would be compromised within a reasonable amount of time on a modern computer.

![Sample Screenshot](./img/samplescreenshot.png)
Additionally, password complexity should also be enforced (e.g. a combination of lower/uppercase characters, numbers and symbols) would also significantly improve the search space to lower the chances of a successful brute-force attacks.

## Administration and Evaluation
### Question 4: Are there any additional features you would like to highlight?

Please fill out this section with details relevant to your team.
#### Proxying of API server (#proxying-of-api-server)

### Team Members
Due to the Same-Origin policy, most modern browsers do not allow `XMLHttpRequest`s to be made across different origins (i.e. combination of protocol, domain and port number) unless Cross-Origin Resource Sharing (CORS) is explicitly allowed by the cross-origin server that is serving the remote resource.

1. Member 1 Name
2. Member 2 Name
3. Member 3 Name
4. Member 4 Name
The reason for this is to prevent unauthorised requests being made on behalf of an unsuspecting user to another website, which would then be able to either extract cookies/session data, or utilise these session data to perform actions on their behalf (e.g. banking sites).

### Short Answer Questions
The immediate implication for the frontend application, since it consumes a REST API via XHR, would be that the Same-Origin Policy would prevent API requests from being made, from `http://localhost:80` to `http://localhost:8080`.

#### Question 1: Briefly describe the web technology stack used in your implementation.
The workaround would be to set up a proxy server running on port `80` that proxies any requests prefixed with `/api` to port `8080`, which does not violate the Same-Origin Policy. This was done using `webpack-dev-server`'s [`proxy` mechanism](https://webpack.js.org/configuration/dev-server/#devserver-proxy).

Answer: Please replace this sentence with your answer.
### Question 5: Is your web application vulnerable? If yes, how and why? If not, what measures did you take to secure it?

#### Question 2: Are there any security considerations your team thought about?
#### Insecure HTTP

Answer: Please replace this sentence with your answer.
Firstly our web application is using HTTP and not HTTPS, meaning any passive sniffer on the same network will be able to view all of the transmission and thus our "secret" diaries are not so secret anymore as it is transferred in cleartext.

#### Question 3: Are there any improvements you would make to the API specification to improve the security of the web application?
Our application is also susceptible to session hijacking/user impersonation as our token is sent in cleartext, and Eve and Mallory can easily impersonate any user that is concurrently using the web application by snooping on the token that is sent through many of the POST requests.

Answer: Please replace this sentence with your answer.
The solution is to simply enforce HTTPS with HTTP redirection to HTTPS. Further defenses such HPKP and HSTS can be employed to prevent HTTPS downgrading and fraudulent certificates from being used.

#### Question 4: Are there any additional features you would like to highlight?
#### Cross-Site Request Forgery (CSRF)

Answer: Please replace this sentence with your answer.
Our web application is also susceptible to CSRF, as a malicious site could induce an unsuspecting user to submit a form or visit a page which fires a `XMLHttpRequest`, firing a request to the site. Although cookies are not used in this application which prevents session riding attacks, any particular URL which results in an undesirable change in the user's state could be potentially exploited, through a simple `GET` request such as through `img` tags.

#### Question 5: Is your web application vulnerable? If yes, how and why? If not, what measures did you take to secure it?
The solution is to employ an anti-CSRF token that should be tied to the user's session. This token should be valid and sent to the server, either as a cookie or a POST data parameter, and the server should validate if this token is valid for the session before allowing the rest of the request to proceed.

Answer: Please replace this sentence with your answer.
### Feedback: Is there any other feedback you would like to give?

#### Feedback: Is there any other feedback you would like to give?
The REST API specification has a few oddities that are quite different from the standard REST API conventions:

Answer: Please replace this sentence with your answer.
* Error status codes should not be `2xx`, but the more idiomatic `4xx` or `5xx` codes
* Not all endpoints return the results in a `result` field (_Update: This has since been rectified._)
* Unable to specify the HTTP method in the endpoint to list all endpoints (`GET /`), resulting in duplicates between `GET` and `POST` endpoints at the same URL (not sure if we should be removing duplicates)

### Declaration
Also, the marks weightage for the UI is rather low at 5%, compared to the REST API at 70%, when it takes up quite a fair amount of time regardless of the frontend stack being used.

#### Please declare your individual contributions to the assignment:
## Declaration

1. Member 1 Name
- Integrated feature x into component y
- Implemented z
2. Member 2 Name
- Wrote the front-end code
3. Member 3 Name
- Designed the database schema
4. Member 4 Name
- Implemented x
### Please declare your individual contributions to the assignment:

1. Au-yong Xiang Rong Alwinson
* Testing and documentation
2. Irvin Lim Wei Quan
* Set up Docker Compose
* Wrote the frontend app
* Set up the basic structure and endpoints for the REST API
3. Tan Ngee Joel Jonas
* Testing and documentation
4. Teng Yong Hao
* Set up the database connections to MongoDB
* Actual REST API functionality for all endpoints
* Set up automate testing scripts

12 changes: 12 additions & 0 deletions api/.babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"presets": [
[
"env",
{
"modules": false,
"targets": { "node": "6.10" }
}
],
"stage-3"
]
}
9 changes: 9 additions & 0 deletions api/.editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
root = true

[*]
charset = utf-8
indent_style = space
indent_size = 2
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
30 changes: 30 additions & 0 deletions api/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Logs
logs
*.log

# Runtime data
pids
*.pid
*.seed

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage

# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# node-waf configuration
.lock-wscript

# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release

# Dependency directory
# https://docs.npmjs.com/cli/shrinkwrap#caveats
node_modules

# Debug log from npm
npm-debug.log
7 changes: 7 additions & 0 deletions api/.prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"useTabs": false,
"printWidth": 80,
"tabWidth": 2,
"singleQuote": true,
"trailingComma": "es5"
}
51 changes: 51 additions & 0 deletions api/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
const express = require('express');
const path = require('path');
const logger = require('morgan');
const cookieParser = require('cookie-parser');
const bodyParser = require('body-parser');
const resultWrapper = require('./middlewares/result_wrapper');
const { isCelebrate } = require('celebrate');

// Set up database connections.
const { host, database } = require('./config');
const mongoose = require('mongoose');
const autoIncrement = require('mongoose-auto-increment');
mongoose.connect(`mongodb://${host}/${database}`);
autoIncrement.initialize(mongoose.connection);

const app = express();

// Set up middlewares.
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(resultWrapper());

// Add routes.
const routes = require('./routes');
app.use('/', routes);

// Catch 404 and forward to error handler.
app.use(function(req, res, next) {
const err = new Error('Not Found');
err.status = 404;
next(err);
});

// Error handler
app.use(function(err, req, res, next) {
// Handle validation errors.
if (isCelebrate(err)) {
err.message = 'Validation failed.';
}

// Create custom JSON error.
const body = { status: false, error: err.message };

// Defaults to 200 OK.
res.status(err.status || 200);
res.json(body);
});

module.exports = app;
Loading