diff --git a/Dockerfile b/Dockerfile index 77837fc..fe3e970 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,6 +5,8 @@ RUN apt-get install -y apache2 RUN pip install -U pip RUN pip install -U flask RUN pip install -U flask-cors +RUN pip install -U pytz +RUN pip install -U passlib RUN echo "ServerName localhost " >> /etc/apache2/apache2.conf RUN echo "$user hard nproc 20" >> /etc/security/limits.conf ADD ./src/service /service diff --git a/README.md b/README.md index 9a814e3..b726a7d 100644 --- a/README.md +++ b/README.md @@ -96,48 +96,56 @@ Please fill out this section with details relevant to your team. ### Team Members -1. Member 1 Name -2. Member 2 Name -3. Member 3 Name -4. Member 4 Name +1. Eldric Lim +2. Joshua Che +3. Chan Jian Hui +4. Derek Kok ### Short Answer Questions #### Question 1: Briefly describe the web technology stack used in your implementation. -Answer: Please replace this sentence with your answer. +Answer: Python Flask for the web application, sqlite3 for the backend-database. We chose sqlite3 as it is light-weight and easy to use with Python Flask. #### Question 2: Are there any security considerations your team thought about? -Answer: Please replace this sentence with your answer. +Answer: Yes, we initially made a user's token "" (empty string) when expired. This allowed requests to be authenticated by simply passing an empty string in a http request. We proceeded to change it to NULL instead. #### Question 3: Are there any improvements you would make to the API specification to improve the security of the web application? -Answer: Please replace this sentence with your answer. +Answer: Make it https-compliant, have more parameters like nonces or timeouts for things like requests or tokens, etc. #### Question 4: Are there any additional features you would like to highlight? -Answer: Please replace this sentence with your answer. +Answer: Our front-end code is incomplete, features such as deletion and changing of permission on diary entries are not implemented. #### Question 5: Is your web application vulnerable? If yes, how and why? If not, what measures did you take to secure it? -Answer: Please replace this sentence with your answer. +Answer: +Yes. To facilitate user access via tokens, a user's token is stored within the webpage as a hidden field. An attacker may be able to output the value of the field by launching an XSS attack, outputting it via an alert message. + +No steps were taken to prevent SQL injection vulnerabilities either. + +As stated in question 2, our application initially allowed blank tokens, which could authenticate a request when a blank token is used, but we fixed this by using NULL instead. #### Feedback: Is there any other feedback you would like to give? -Answer: Please replace this sentence with your answer. +Answer: None. ### Declaration #### Please declare your individual contributions to the assignment: -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 +1. Eldric Lim + - API + - Front-end +2. Joshua Che + - API + - Front-end +3. Chan Jian Hui + - API + - Front-end +4. Derek Kok + - API + - Front-end diff --git a/Sample cURL requests b/Sample cURL requests new file mode 100644 index 0000000..ee457be --- /dev/null +++ b/Sample cURL requests @@ -0,0 +1,77 @@ +==VERY IMPORTANT POINTS== + +Each GET/POST request is sent with correct parameters as specified in the assignment API. Wrong request parameters may cause KeyErrors or other errors. + +Queries/operations done on non-existent records --but with a valid token-- will return HTTP 200 OK. (e.g. /diary/delete with valid token but non-existent diary id) + +Database file is in src/service/database.db, and it contains 2 tables "users" and "diaries". +Use DB Browser for SQLite to see how the tables look like, I've included 3 users by default in the "users" table. +The "diaries" table is empty, and requires us to populate it by using sample queries that I've included. + +Since this web server is run from Docker, I've taken the liberty to make the database volatile; you can only use DB Browser to look and edit the table layouts, but won't be able to see real-time data inside the tables. To do that, observe the output when you submit curl requests, or enter the URL in firefox to see if the output is correct. + + + +==INSTRUCTIONS== + +Run these POST curl requests in a terminal, or submit your own http GET/POST request with any tool. GET requests can be done by simply opening the respective page in Firefox. (e.g. http://localhost:8080/meta/members) + + + +==REGISTER A USER== + +curl -i -X POST -H 'Content-Type: application/json' -d '{"username": "testuser", "password": "testpass", "fullname": "test full name", "age": 2009}' http://127.0.0.1:8080/users/register + + + +==AUTHENTICATE A USER== + +curl -i -X POST -H 'Content-Type: application/json' -d '{"username": "testuser", "password": "testpass"}' http://127.0.0.1:8080/users/authenticate + + + +==RETURNS WHICHEVER USER REPRESENTATED BY THIS TOKEN== + +curl -i -X POST -H 'Content-Type: application/json' -d '{"token":"111111111111111111111111111111111111"}' http://127.0.0.1:8080/users + + + +==CREATES A --PUBLIC-- DIARY FOR AN AUTHENTICATED USER== + +curl -i -X POST -H 'Content-Type: application/json' -d '{"token":"111111111111111111111111111111111111", "title": "No One Can See This Post", "public": true, "text": "It is very secret!"}' http://127.0.0.1:8080/diary/create + + + +==CREATES A --PRIVATE-- DIARY FOR AN AUTHENTICATED USER== + +curl -i -X POST -H 'Content-Type: application/json' -d '{"token":"111111111111111111111111111111111111", "title": "No One Can See This Post", "public": true, "text": "It is very secret!"}' http://127.0.0.1:8080/diary/create + + + +==LISTS ALL DIARY ENTRIES THAT AN AUTHENTICATED USER HAS== + +curl -i -X POST -H 'Content-Type: application/json' -d '{"token":"111111111111111111111111111111111111"}' http://127.0.0.1:8080/diary + + + +==SETS AN AUTHENTICATED USER'S DIARY ENTRY TO PRIVATE== + +curl -i -X POST -H 'Content-Type: application/json' -d '{"token":"111111111111111111111111111111111111", "id":1, "public":false}' http://127.0.0.1:8080/diary/permission + + + +==SETS AN AUTHENTICATED USER'S DIARY ENTRY TO PUBLIC== + +curl -i -X POST -H 'Content-Type: application/json' -d '{"token":"111111111111111111111111111111111111", "id":1, "public":false}' http://127.0.0.1:8080/diary/permission + + + +==DELETE AN AUTHENTICATED USER'S DIARY ENTRY== + +curl -i -X POST -H 'Content-Type: application/json' -d '{"token":"111111111111111111111111111111111111", "id":1}' http://127.0.0.1:8080/diary/delete + + + +==EXPIRES AN AUTHENTICATED USER'S TOKEN== + +curl -i -X POST -H 'Content-Type: application/json' -d '{"token":"111111111111111111111111111111111111"}' http://127.0.0.1:8080/users/expire diff --git a/img/samplescreenshot.png b/img/samplescreenshot.png old mode 100644 new mode 100755 index 4b69df0..70b200d Binary files a/img/samplescreenshot.png and b/img/samplescreenshot.png differ diff --git a/src/html/demo.js b/src/html/demo.js index ada1b22..e952d3b 100644 --- a/src/html/demo.js +++ b/src/html/demo.js @@ -21,6 +21,26 @@ function ajax_get(url, callback) { xmlhttp.send(); } +function ajax_post(url, jsondata, callback) { + var xmlhttp = new XMLHttpRequest(); + xmlhttp.onreadystatechange = function() { + if (xmlhttp.readyState == 4 && xmlhttp.status == 200) { + console.log('responseText:' + xmlhttp.responseText); + try { + var data = JSON.parse(xmlhttp.responseText); + } catch(err) { + console.log(err.message + " in " + xmlhttp.responseText); + return; + } + callback(data); + } + }; + + xmlhttp.open("POST", url, true); + xmlhttp.setRequestHeader("Content-Type", "application/json;charset=UTF-8"); + xmlhttp.send(jsondata); +} + ajax_get(API_ENDPOINT + '/meta/heartbeat', function(data) { if (data.status) { document.getElementById("demo_heartbeat").innerHTML = "Heartbeat success"; @@ -45,3 +65,180 @@ ajax_get(API_ENDPOINT + '/meta/members', function(data) { } }); +ajax_get(API_ENDPOINT + '/diary', function(data) { + if (data.status) { + var diaries = data.result; + var output = "
Requirements for the front end are as follows:
-