We will be doing all of the client-side password hashing using a JavaScript library called CryptoJS. Go to the download page(10) and download the latest version (it should be v3.1.2). Unzip the file (it doesn’t matter much where, as you only need one of the files in it) and navigate into the rollups sub-directory. This directory contains several different .js files, each of which handles a different cryptography algorithm.
To do password hashing though, we only need the sha256.js file. Copy that over to your IoT_project folder that the rest of your beautiful code is in so that it can be used in the rest of your website. Then you can include the sha256.js file in index.html
The next step is to make a username and password entry field, similar to what you would encounter on most websites. Go down to the body section of your document, and add in a textbox with the label “Username:” and a password box with the label “Password:”. Make their id’s “usernameFeild” and “passwordFeild” respectively. Then, add a button below them with the label “Sign In” and the id “signInButton”.
Now that the entry fields are set up to show on the webpage, you can go to main.js and start sending credentials to your web socket. We are going to write most of the JavaScript needed for sending credentials first, and then we’ll write the python code for checking if the credentials are valid.
First, we need to change the function tied to the eventListener for websocket. You see, we only want to setup the LED toggle once the user has been properly validated. Otherwise, they could skip the login and just control the LED with or without credentials. So there needs to be a setupSignIn function that gets passed into the event listener instead. Change out the parameter setupToggleLED(websocket) with setupSignIn(websocket), and then create the new function below.
Now we need to actually write some code into the setupSignIn function. We can add another eventListener in there to trigger some code when the Sign In button is pressed. We can easily make two variables that store the username, and the hash of the password (which is run using the CryptoJS library). We can then put those into a JSON object and send them to the Pi server.
Now would be a good time to just open up the webpage and double check that you can enter values and send with the Sign In button. Because app.py is printing all of the web socket messages, you should see the sent credentials showing up there.
Depending on how you are using this IoT base, it can be useful to have multiple users. Say, for example, that you previously allowed your younger siblings to control the lights, but they have begun to abuse the power and you want to deny them access. Instead of changing the general password for the web socket, you can just delete their user entry from the database. That is the kind of functionality that we are about to build.
We are going to be storing all of our users in a .json file called users.json. Create the users.json file in the same directory as app.py (IoT_project if you have been following this module to the t, but if not that’s chill) and then open it in geany. JSON files are pretty much the same as JSON objects, where the entire file contains one JSON object. Our file will hold an array containing all of the different users, each of whom is represented as another JSON object with a username and password_hash field (the hashes are cut short due to space restrictions, you do need a } at the end).
As we covered earlier, the passwords cannot be just entered in plain text form. To get the hash for a specific password, you can either open the console on your browser and do it with CryptoJS (not reccommended) or just use the built in linux utility (strongly recommended). Just go to the terminal and type in the following command
Note: do not use “mypassword” as your password, it is very insecure and is just acting as a placeholder
Copy that password (only the numbers and letters, not the - ), and then paste it into the password_hash field on the user object. You now have your data base, but it still needs to be loaded to app.py
The json library for python actually has a built-in function for handling this. All you need to do is use the below code to open users.json in read mode, and then set the users variable to the array of users that is stored in users.json. This should go just below your imports.
First, we need to create a function that can take in a username and a password hash and check whether there is a user with those credentials. This is relatively simple. We can just loop through all of the users and check if the credentials match, and if none of them do then return None.
Now the credential check needs to be added to the handler. Currently, that function just contains a while True loop that handles controlling the LED. We can make sure that no client can access that part of the code until they have given proper credentials by using get_user and closing the connection if it returns None. Remember, we once again have to check the type field of the incoming JSON object.
Once the credentials are validated, the client website still needs to know whether the credentials were valid and act on that. This response can be sent quite easily on the server side using websocket.send, and we can also add a message that is sent when the credentials are invalid.
You might notice that this doesn’t send a JSON object. I did it like that because an object doesn’t feel necessary for this small affirmative response, but if you would like to make it JSON for consistence then go for it.
Now, going back to main.js, we need to make it handle the message. In JavaScript, you can handle messages by setting websocket.onmessage to a function. That function, we can write an if statement that checks if the message is an affirmative one, and then setup the toggle.
Currently, this works okay. You can enter your username and password and control the LED if you get it right. There are a few issues though. For one, both the controls and the login are visible at the same time. Also, once you have hit Sign In the username and password are still sitting there in the text boxes.
To fix the first problem, we can put the sign in portion of the page and the controls section of the page into separate divs, which lets us control which of them is visible at any given moment (the username and passwords fields were made their own sub-divs so that the webpage formats them more nicely).
Next, we can then go into main.js and make signInPage start as visible and controlsPage start as hidden. We can then switch which one is visible, as clear the values of the username and password fields by setting their values to “”.
Our final step is to make a system that allows the user multiple login attempts before the connection is closed. We can do this pretty simply but putting the authentication code in app.py and putting it in a for loop. First, we need to make a variable controlling how many attempts are allowed. You can just initialize that towards the top of the file, which will be helpful if you ever want to go back and adjust the number of allowed login attempts.
With that, you can take all of the authentication code in the handler and put it in a for loop that loops according to attempts_allowed (this can be made easier if you highlight all the code you want in the for loop and hit TAB to indent the entire block).
After that, we will have to change how the websocket disconnects. It cannot just do it when some credentials are found invalid, because the program may be only halfway through the loop and still have several chance to give the client. Thus, we need to move the block of code that closes the connection into a new if statement that checks if the loop has finished and then closes the connection. We also need to add a break statement onto the case for valid credentials. Put together, those few tweaks look like this:
Finally, on the client side, we can add a little HTML element between the sign in fields and the LED controls that shows whether the credentials are found to be invalid. We can just add this element with the id “invalidSignIn” and use JavaScript to make is start invisible and then appear when the websocket recieves a negative message.
Congratulations! You now have your very own IoT control of a Raspberry Pi. Since the server side is in python and you can send any message across the web socket connection, there are very few limits to where you can take this code. Have fun! :)
https://www.ibm.com/blogs/internet-of-things/what-is-the-iot/
https://www.geeksforgeeks.org/what-is-web-socket-and-how-it-is-different-from-the-http/
https://websockets.readthedocs.io/en/stable/intro/tutorial1.html#transmit-from-server-to-browser
https://www.thesslstore.com/blog/what-is-a-hash-function-in-cryptography-a-beginners-guide/