# Please check my HTTP login Flow

**URL:** <https://discourse.nodered.org/t/please-check-my-http-login-flow/86553>\
**Category:** General\
**Tags:** node-red-dashboard, security, database, function-node\
**Created:** [19 March 2024 07:35 UTC](https://discourse.nodered.org/t/please-check-my-http-login-flow/86553 "2024-03-19T07:35:27Z")\
**Posts on this page:** 14\
**Page:** 1

<div class="post-metadata">

**Author:** ![2shlee](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/2shlee/32/89413_2.png) [@2shlee](https://discourse.nodered.org/u/2shlee)\
**Post date:** [19 March 2024 07:35 UTC](https://discourse.nodered.org/t/please-check-my-http-login-flow/86553/1 "2024-03-19T07:35:28Z")

</div>

It implements a login function that authenticates users compared to the value of the database.  
When I run node red and enter id/pw for the first time, it works fine. Compare the values with DB to move on only when they match. However, I come back to the login screen and leave the input empty and just press the login button to move on to the next screen again. (As if I succeeded in logging in.) I wonder where the hell the problem is.  
I think it's a problem with the global variable that you set as the input not being initialized, or there's an error in the logic itself.  
Please help me.

```auto
[
    {
        "id": "75ce2ad0555f4266",
        "type": "http in",
        "z": "b1537c574e27a2f9",
        "name": "",
        "url": "/login_action",
        "method": "post",
        "upload": false,
        "swaggerDoc": "",
        "x": 210,
        "y": 580,
        "wires": [
            [
                "ad388a18420d9f62"
            ]
        ]
    },
    {
        "id": "ad388a18420d9f62",
        "type": "function",
        "z": "b1537c574e27a2f9",
        "name": "입력한 value에 대해 set global",
        "func": "if (msg.payload.userID != \"\" && msg.payload.password != \"\") {\n global.set(\"loginUserID\", msg.payload.userID);\n global.set(\"loginPassword\", msg.payload.password);\n //global.set(\"msg_login_action\",msg);\n} \nreturn msg;\n",
        "outputs": 1,
        "timeout": 0,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 450,
        "y": 580,
        "wires": [
            [
                "0a3dc9fe4f6967a6"
            ]
        ]
    },
    {
        "id": "0a3dc9fe4f6967a6",
        "type": "function",
        "z": "b1537c574e27a2f9",
        "name": "입력한 Username이 mySQL에 존재하는지 확인",
        "func": "var id = global.get(\"loginUserID\");\nvar password = global.get(\"loginPassword\");\nmsg.topic = \"SELECT * FROM users WHERE userID = ?\";\nif (id && password) {\n msg.payload = [id];\n} else {\n // ID나 Password가 없는 경우, 빈 배열 반환\n msg.payload = [];\n}\n//msg.topic = \"SELECT * FROM users WHERE userName = ? AND password = ?\";\n//msg.payload = [id];\nreturn msg;\n",
        "outputs": 1,
        "timeout": 0,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 780,
        "y": 580,
        "wires": [
            [
                "442a018c69b061ef"
            ]
        ]
    },
    {
        "id": "442a018c69b061ef",
        "type": "mysql",
        "z": "b1537c574e27a2f9",
        "mydb": "23139c6750c1e4b8",
        "name": "",
        "x": 290,
        "y": 640,
        "wires": [
            [
                "45720baf77e5d803"
            ]
        ]
    },
    {
        "id": "45720baf77e5d803",
        "type": "function",
        "z": "b1537c574e27a2f9",
        "name": "Authentication",
        "func": "/*if(msg.payload!=null){\n msg.payload=\"Login Success\";\n}else {\n msg.payload =\"Login Failed\";\n}\nreturn msg;*/\n// 사용자를 인증하는 함수\nvar loginUserID = global.get(\"loginUserID\");\nvar loginPassword = global.get(\"loginPassword\");\n\n\nif (msg.payload.length > 0) {\n var userID = msg.payload[0].userID;\n var userPassword = msg.payload[0].password;\n if (userPassword === loginPassword) { // 입력된 비밀번호와 비교\n\n var encodedUserID = Buffer.from(userID).toString('base64');\n msg.cookies = {\n COOKIE: {\n value: encodedUserID,\n maxAge: 900000 // 쿠키 유효 시간 (밀리초), 여기서는 15분\n }\n };\n msg.redirect = \"http://localhost:1880/ui\"; // 로그인 성공 시 대시보드로 리다이렉트\n } else {\n msg.payload = \"2\";\n }\n} else {\n msg.payload = \"1\";\n}\n\n\nreturn msg;\n\n\n",
        "outputs": 1,
        "timeout": "",
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 440,
        "y": 640,
        "wires": [
            [
                "91d7e94b135bbb57"
            ]
        ]
    },
    {
        "id": "91d7e94b135bbb57",
        "type": "switch",
        "z": "b1537c574e27a2f9",
        "name": "",
        "property": "payload",
        "propertyType": "msg",
        "rules": [
            {
                "t": "else"
            },
            {
                "t": "eq",
                "v": "1",
                "vt": "str"
            },
            {
                "t": "eq",
                "v": "2",
                "vt": "str"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 3,
        "x": 590,
        "y": 640,
        "wires": [
            [
                "1021190647257c4d"
            ],
            [
                "ddbd12536354d9fa"
            ],
            [
                "2aaf8130381ccb06"
            ]
        ]
    },
    {
        "id": "ddbd12536354d9fa",
        "type": "function",
        "z": "b1537c574e27a2f9",
        "name": "= 1 : Unknown User",
        "func": "msg.payload = \"Login Failed: User Not Found.\";\nreturn msg;",
        "outputs": 1,
        "timeout": "",
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 770,
        "y": 660,
        "wires": [
            [
                "665ed3a23ab05c5f"
            ]
        ]
    },
    {
        "id": "2aaf8130381ccb06",
        "type": "function",
        "z": "b1537c574e27a2f9",
        "name": "= 2 : Wrong Password",
        "func": "msg.payload = \"Login Failed: Invalid Password.\";\n\nreturn msg;",
        "outputs": 1,
        "timeout": "",
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 760,
        "y": 700,
        "wires": [
            [
                "665ed3a23ab05c5f"
            ]
        ]
    },
    {
        "id": "665ed3a23ab05c5f",
        "type": "function",
        "z": "b1537c574e27a2f9",
        "name": "Login Failed",
        "func": "var msg1 = msg.payload;\nvar msg=global.get(\"msg_login_action\") || \"\";\nmsg.payload = msg1;\nreturn msg;",
        "outputs": 1,
        "timeout": 0,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 950,
        "y": 680,
        "wires": [
            [
                "f06841e5dc3d9282"
            ]
        ]
    },
    {
        "id": "f06841e5dc3d9282",
        "type": "template",
        "z": "b1537c574e27a2f9",
        "name": "page",
        "field": "payload",
        "fieldType": "msg",
        "format": "handlebars",
        "syntax": "mustache",
        "template": "<script>\n // 웹 페이지가 로드될 때 실행되는 함수\n window.onload = function() {\n // 서버에서 전달받은 메시지를 가져옴\n var message = \"{{payload}}\"; // 이 부분에 Node-RED에서 전달한 메시지를 넣어야 함\n\n // 로그인 결과에 따라 팝업 창을 표시\n if (message.includes(\"Login Failed\")) {\n // 로그인 실패 시 팝업 창 표시\n alert(message);\n \n // 팝업 확인 후 로그인 페이지로 자동 이동\n window.location.href = \"http://192.168.0.18:1880/login\"; // 로그인 페이지 URL로 변경해야 함\n }\n };\n</script>\n\n",
        "output": "str",
        "x": 1090,
        "y": 680,
        "wires": [
            [
                "08750f2d966af0ce"
            ]
        ]
    },
    {
        "id": "1021190647257c4d",
        "type": "template",
        "z": "b1537c574e27a2f9",
        "name": "page",
        "field": "payload",
        "fieldType": "msg",
        "format": "handlebars",
        "syntax": "mustache",
        "template": "<script>\nwindow.location.href = \"http://192.168.0.18:1880/ui\";\n</script>",
        "output": "str",
        "x": 950,
        "y": 640,
        "wires": [
            [
                "50ef68fa45d8e864",
                "c913d0c7d0a84945"
            ]
        ]
    },
    {
        "id": "50ef68fa45d8e864",
        "type": "http response",
        "z": "b1537c574e27a2f9",
        "name": "Go to Dashboard",
        "statusCode": "",
        "headers": {},
        "x": 1130,
        "y": 620,
        "wires": []
    },
    {
        "id": "08750f2d966af0ce",
        "type": "http response",
        "z": "b1537c574e27a2f9",
        "name": "Return to Login",
        "statusCode": "",
        "headers": {},
        "x": 1240,
        "y": 680,
        "wires": []
    },
    {
        "id": "7037a375ba2d0cc2",
        "type": "http in",
        "z": "b1537c574e27a2f9",
        "name": "",
        "url": "/login",
        "method": "get",
        "upload": false,
        "swaggerDoc": "",
        "x": 180,
        "y": 540,
        "wires": [
            [
                "39dd5592efadfa8d"
            ]
        ]
    },
    {
        "id": "39dd5592efadfa8d",
        "type": "template",
        "z": "b1537c574e27a2f9",
        "name": "<style>",
        "field": "payload.style",
        "fieldType": "msg",
        "format": "handlebars",
        "syntax": "mustache",
        "template": "html {\n display: flex;\n justify-content: center;\n align-items: center;\n font-size: 150%;\n height: 70vh; /* 보는 사람의 페이지의 크기에 따라 변경됨. */\n}\n\nbody {\n font-size: 14px;\n font-family: 'Roboto', sans-serif;\n background-color: #023047;\n}\n\n.logo-wrapper {\n text-align: center;\n margin-bottom: 20px;\n}\n\n.logo {\n width: 200px; /* 로고 이미지의 너비를 조절 */\n display: inline-block; /* 로고를 가로 중앙 정렬하기 위해 inline-block 속성 적용 */\n\n}\n.logo img {\n width: 100%; /* 로고 이미지의 너비를 부모 요소에 맞게 조절 */\n height: auto; /* 이미지 비율 유지 */\n}\n\n.login-wrapper {\n text-align: center; /* 부모 요소에 대해 가운데 정렬을 적용 */\n margin-bottom: 20px;\n width: 400px;\n padding: 40px;\n box-sizing: border-box;\n background-color: #fff; /* 흰 배경 추가 */\n border-radius: 10px; /* 모서리를 둥글게 만듦 */\n box-shadow: 0px 0px 10px 0px rgba(0,0,0,0.1); /* 그림자 효과 추가 */\n display: flex; /* 자식 요소를 가로로 배치하기 위해 flex 속성 적용 */\n flex-direction: column; /* 자식 요소를 세로로 배치 */\n align-items: center; /* 자식 요소를 가운데 정렬 */\n}\n\n#login-form {\n width: 100%; /* 폼의 너비를 100%로 설정 */\n margin-bottom: 20px; /* 하단 여백 추가 */\n}\n#login-form > input {\n width: 100%;\n height: 48px;\n padding: 0 10px;\n box-sizing: border-box;\n margin-bottom: 10px;\n border-radius: 6px;\n background-color: #F8F8F8;\n border: 2px solid #023047; /* 모든 테두리에 대한 색상 설정 */\n font-family: 'Lucida Sans', sans-serif; /* 원하는 폰트로 변경 */\n\n}\n\n#login-form > input::placeholder {\n color: #D2D2D2;\n}\n\n#login-form > input[type=\"submit\"] {\n color: #fff;\n font-size: 16px;\n background-color: #FFB703;\n margin-top: 20px;\n font-family: 'Lucida Sans', sans-serif; /* 원하는 폰트로 변경 */\n border: none; /* 테두리 제거 */\n width: 100%; /* 버튼의 너비를 100%로 설정하여 폼과 동일한 너비를 가지도록 함 */\n}\n\n.submit-btn {\n color: #fff; \n font-size: 16px;\n margin-top: 2px;\n background-color: #FFB703; \n border: 2px solid #FFB703; /* 황색 테두리 */\n border-radius: 6px; /* 모서리를 둥글게 만듦 */\n //padding: 12px 0; /* 위아래 12px, 좌우 0 */\n width: 100%; /* 버튼의 너비를 100%로 설정하여 폼과 동일한 너비를 가지도록 함 */\n height: 48px;\n cursor: pointer; /* 커서 모양을 포인터로 변경 */\n font-family: 'Lucida Sans', sans-serif; /* 원하는 폰트로 변경 */\n}\n\n.submit-btn:hover {\n background-color: #ffa500; /* 호버 시 배경색 변경 */\n}\n\n#login-form > input[type=\"checkbox\"] {\n display: none;\n}\n\n#login-form > label {\n color: #999999;\n}\n\n#login-form input[type=\"checkbox\"] + label {\n cursor: pointer;\n padding-left: 26px;\n background-image: url(\"checkbox.png\");\n background-repeat: no-repeat;\n background-size: contain;\n font-family: 'Lucida Sans', sans-serif; /* 원하는 폰트로 변경 */\n\n}\n\n#login-form input[type=\"checkbox\"]:checked + label {\n background-image: url(\"checkbox-active.png\");\n background-repeat: no-repeat;\n background-size: contain;\n font-family: 'Lucida Sans', sans-serif; /* 원하는 폰트로 변경 */\n}\n/* ###### 5. direction ###### */\n.direction __container {\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: column;\n justify-content: space-between;\n}\n.direction__ container p {\n text-align: center;\n margin-top: 10px;\n}",
        "output": "str",
        "x": 560,
        "y": 540,
        "wires": [
            [
                "ae97fec1521bf021"
            ]
        ]
    },
    {
        "id": "ae97fec1521bf021",
        "type": "template",
        "z": "b1537c574e27a2f9",
        "name": "",
        "field": "payload",
        "fieldType": "msg",
        "format": "handlebars",
        "syntax": "mustache",
        "template": "<!DOCTYPE html>\n<html>\n<head>\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<style> {{{payload.style}}} </style>\n</head>\n<body>\n <div class=\"login-wrapper\">\n <div class=\"logo-wrapper\">\n <div class=\"logo\">\n <img src=\"http://theconsol.co.kr/public/img/logo/logo.png\">\n </div>\n </div>\n <form method=\"post\" action=\"http://192.168.0.18:1880/login_action\" id=\"login-form\">\n <input type=\"text\" name=\"userID\" placeholder=\"ID\">\n <input type=\"password\" name=\"password\" placeholder=\"Password\">\n <label for=\"remember-check\">\n <input type=\"checkbox\" id=\"remember-check\">아이디 저장하기\n </label>\n <input type=\"submit\" id=\"login-button\" value=\"Login\">\n </form>\n <button class=\"submit-btn\" onclick=\"goToSignUpPage()\">Sign up</button> \n </div>\n<script>\n document.addEventListener(\"DOMContentLoaded\", function() {\n console.log(\"JavaScript 코드가 로드되었습니다.\");\n // 폼 제출 시 입력 필드가 비어 있는지 확인하고, 비어 있으면 제출을 막음\n document.getElementById(\"login-form\").addEventListener(\"submit\", function(event) {\n if (useridInput.value.trim() === \"\" || passwordInput.value.trim() === \"\") {\n // 입력 필드가 비어 있으면 폼 제출을 막음\n event.preventDefault();\n // 사용자에게 메시지 표시 (예를 들어 alert 사용)\n alert(\"ID와 비밀번호를 입력하세요.\");\n }\n });\n // Sign up 페이지로 이동하는 함수\n function goToSignUpPage() {\n console.log(\"Sign up 페이지로 이동합니다.\");\n window.location.href = \"http://192.168.0.18:1880/signup\"; // \"/signup\"으로 변경\n }\n \n // 필수 입력 필드\n var useridInput = document.getElementById(\"userID\");\n var passwordInput = document.getElementById(\"password\");\n var loginButton = document.getElementById(\"login-button\");\n\n // 입력 필드의 변경 이벤트를 감지하여 제출 버튼 활성화 상태 변경\n useridInput.addEventListener(\"input\", toggleSubmitButton);\n passwordInput.addEventListener(\"input\", toggleSubmitButton);\n\n function toggleSubmitButton() {\n // 사용자명과 비밀번호가 모두 채워져 있을 때 제출 버튼 활성화\n if (useridInput.value.trim() !== \"\" && passwordInput.value.trim() !== \"\") {\n loginButton.disabled = false;\n } else {\n loginButton.disabled = true;\n }\n }\n\n\n });\n</script>\n</body>\n</html>\n",
        "output": "str",
        "x": 860,
        "y": 540,
        "wires": [
            [
                "98add455cdc50a57"
            ]
        ]
    },
    {
        "id": "a05c6ac2e72411e4",
        "type": "http in",
        "z": "b1537c574e27a2f9",
        "name": "HTTP Input",
        "url": "/login_action",
        "method": "post",
        "upload": false,
        "swaggerDoc": "",
        "x": 190,
        "y": 760,
        "wires": [
            [
                "5671a69f69d959b8"
            ]
        ]
    },
    {
        "id": "3ef1ab865351c4d2",
        "type": "http response",
        "z": "b1537c574e27a2f9",
        "name": "HTTP Response",
        "statusCode": "",
        "headers": {},
        "x": 590,
        "y": 760,
        "wires": []
    },
    {
        "id": "5671a69f69d959b8",
        "type": "function",
        "z": "b1537c574e27a2f9",
        "name": "Check Cookie",
        "func": "var loginCookie = msg.req.cookies.COOKIE; // 쿠키 이름이 \"COOKIE\"인 경우\nif (loginCookie) {\n // 쿠키를 확인하여 로그인 상태를 처리하는 추가적인 로직 구현\n // 여기에는 쿠키를 해석하여 로그인 상태를 확인하는 로직 등이 들어갈 수 있습니다.\n\n // 이미 로그인한 상태라면 대시보드로 리다이렉트\n return { payload: null, redirect: \"http://192.168.0.18/ui\" };\n} else {\n // 로그인이 필요한 페이지를 보여줌\n return { payload: \"you have to login\" };\n}\n",
        "outputs": 1,
        "timeout": "",
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 360,
        "y": 760,
        "wires": [
            [
                "3ef1ab865351c4d2"
            ]
        ]
    },
    {
        "id": "98add455cdc50a57",
        "type": "http response",
        "z": "b1537c574e27a2f9",
        "name": "",
        "statusCode": "",
        "headers": {},
        "x": 1070,
        "y": 540,
        "wires": []
    },
    {
        "id": "c913d0c7d0a84945",
        "type": "debug",
        "z": "b1537c574e27a2f9",
        "name": "debug 2",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 1240,
        "y": 560,
        "wires": []
    },
    {
        "id": "23139c6750c1e4b8",
        "type": "MySQLdatabase",
        "name": "",
        "host": "127.0.0.1",
        "port": "3306",
        "db": "project",
        "tz": "",
        "charset": "UTF8"
    }
]

```

---

<div class="post-metadata">

**Author:** ![TotallyInformation](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/totallyinformation/32/31_2.png) [@TotallyInformation](https://discourse.nodered.org/u/TotallyInformation)\
**Post date:** [19 March 2024 11:32 UTC](https://discourse.nodered.org/t/please-check-my-http-login-flow/86553/2 "2024-03-19T11:32:35Z")

</div>

Still trying to work through the logic. However:

- You are not using HTTPS which means that all passwords are already potentially compromised. HTTPS must be implemented before any login.
- `action="http://192.168.0.18:1880/login_action"` is very fragile. I think that `action="./login_action"` would be more reliable?

---

<div class="post-metadata">

**Author:** ![TotallyInformation](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/totallyinformation/32/31_2.png) [@TotallyInformation](https://discourse.nodered.org/u/TotallyInformation)\
**Post date:** [19 March 2024 11:40 UTC](https://discourse.nodered.org/t/please-check-my-http-login-flow/86553/3 "2024-03-19T11:40:34Z")

</div>

The function named "입력한 value에 대해 set global" - when setting, you've only allowed for a SINGLE USER. If multiple people try to log in, only the last person to do so would be recorded - is that what you wanted?

And, you haven't validated the inputs. While you've tried to validate user input in the browser, this is not sufficient and isn't secure. You MUST validate on receipt. Make sure the required inputs are not blank, do not exceed whatever sizes you've specified in your database and that they contain valid text that your DB can handle.

---

<div class="post-metadata">

**Author:** ![TotallyInformation](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/totallyinformation/32/31_2.png) [@TotallyInformation](https://discourse.nodered.org/u/TotallyInformation)\
**Post date:** [19 March 2024 11:43 UTC](https://discourse.nodered.org/t/please-check-my-http-login-flow/86553/4 "2024-03-19T11:43:37Z")

</div>

The function named "입력한 Username이 mySQL에 존재하는지 확인" seems to contain the most fundamental and terrible mistake any developer can make!

You appear to be appending raw user input to a SQL query?

![](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/3X/9/6/96baa5b855cd9553b8507a43c1938ab9d7ec9e15.png)  
You MUST use a prepared statement for your query (that is more efficient anyway) and you MUST ensure that the above is not possible.

---

<div class="post-metadata">

**Author:** ![2shlee](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/2shlee/32/89413_2.png) [@2shlee](https://discourse.nodered.org/u/2shlee)\
**Post date:** [21 March 2024 01:24 UTC](https://discourse.nodered.org/t/please-check-my-http-login-flow/86553/5 "2024-03-21T01:24:22Z")

</div>

Thank you for your advice.  
That's a very bad coding for security.  
How do I query SQL to be safe..?

---

<div class="post-metadata">

**Author:** ![TotallyInformation](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/totallyinformation/32/31_2.png) [@TotallyInformation](https://discourse.nodered.org/u/TotallyInformation)\
**Post date:** [21 March 2024 08:53 UTC](https://discourse.nodered.org/t/please-check-my-http-login-flow/86553/6 "2024-03-21T08:53:10Z")

</div>

> [@2shlee](#):
>
> How do I query SQL to be safe..?

> **[mysql prepared statements at DuckDuckGo](https://duckduckgo.com/?q=mysql%2Bprepared%2Bstatements&t=vivaldi&ia=web)**
>
> DuckDuckGo. Privacy, Simplified.

> **[mysql sql injection prevention at DuckDuckGo](https://duckduckgo.com/?q=mysql%2Bsql%2Binjection%2Bprevention&t=vivaldi&ia=web)**
>
> DuckDuckGo. Privacy, Simplified.

---

<div class="post-metadata">

**Author:** ![2shlee](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/2shlee/32/89413_2.png) [@2shlee](https://discourse.nodered.org/u/2shlee)\
**Post date:** [21 March 2024 23:55 UTC](https://discourse.nodered.org/t/please-check-my-http-login-flow/86553/7 "2024-03-21T23:55:05Z")

</div>

Then, using the prepared statement like [PREPARE insert\_user FROM 'INSERT INTO users (username, email) VALUES (?, ?)';] instead of the [SELECT ~~], right?  
I'll give it a try.  
Thank you for your help!!

---

<div class="post-metadata">

**Author:** ![TotallyInformation](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/totallyinformation/32/31_2.png) [@TotallyInformation](https://discourse.nodered.org/u/TotallyInformation)\
**Post date:** [22 March 2024 00:51 UTC](https://discourse.nodered.org/t/please-check-my-http-login-flow/86553/8 "2024-03-22T00:51:44Z")

</div>

Yes, but make sure you trim and sanitise the input strings. For example, your user names probably no longer than 64 chars and your passwords probably the same. Passwords also not less than 8 chars. Email address also no longer than 64 chars perhaps and must contain an `@`. And none of the inputs should contain `"`, `;` or `'`. Also inputs should only contain valid visible characters.

Passwords also should never be stored as text. They should always be hashed, your server never needs to know the actual password, only the hash. That way, the password string cannot be reconstituted from the stored hashes.

---

<div class="post-metadata">

**Author:** ![2shlee](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/2shlee/32/89413_2.png) [@2shlee](https://discourse.nodered.org/u/2shlee)\
**Post date:** [22 March 2024 01:20 UTC](https://discourse.nodered.org/t/please-check-my-http-login-flow/86553/9 "2024-03-22T01:20:54Z")

</div>

So, all I have to do is

1. The id/pw-related input value stored in the global variable should be deleted.
2. It is necessary to verify the input value.
3. You have to encrypt and save your password.

I have a question!  
How do I verify the input value of a password or ID in Nodered?  
And the sqlquery that I used before  
[msg.topic = "SELECT \* FROM users WHERE UserID = ? OR Email = ?";]  
Is it also a security risk to write it like this?

---

<div class="post-metadata">

**Author:** ![2shlee](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/2shlee/32/89413_2.png) [@2shlee](https://discourse.nodered.org/u/2shlee)\
**Post date:** [22 March 2024 04:37 UTC](https://discourse.nodered.org/t/please-check-my-http-login-flow/86553/10 "2024-03-22T04:37:55Z")

</div>

I have one more question....  
I want to display the successful login username on the dashboard. And I also want to have my user ID and logout time logged in db when I log out, do I have to reset the global variable? Is there any other way?

---

<div class="post-metadata">

**Author:** ![TotallyInformation](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/totallyinformation/32/31_2.png) [@TotallyInformation](https://discourse.nodered.org/u/TotallyInformation)\
**Post date:** [22 March 2024 09:03 UTC](https://discourse.nodered.org/t/please-check-my-http-login-flow/86553/11 "2024-03-22T09:03:54Z")

</div>

> [@2shlee](#):
>
> How do I verify the input value of a password or ID in Nodered?

The point of a hash is that it is a 1-way cryptographic formula. As long as you apply the same formula to the same input, you get the same hash. So it is the hashes that you compare. The hash is stored in the db instead of the password text. When a user logs in, you query the db for the user details (based on their id), then re-hash their inputted password and compare against the hash from the db. Node.js has a native crypto library you can use or there may well be a suitable contributed node. The important thing being that you hash the password as early as possible and destroy the clear-text password to prevent sniffing.

> [@2shlee](#):
>
> [msg.topic = "SELECT \* FROM users WHERE UserID = ? OR Email = ?";]  
> Is it also a security risk to write it like this?

Yes, it is still susceptable to injection attacks. Always use a prepared statement (which is more efficient anyway), and add quotes to your input strings (having sanitised the input strings as well of course.

If I remember rightly (been a while since I used SQL, this would be better - but only as a prepared statement and with sanitised inputs:

```sql
SELECT LIMIT 1 * FROM users WHERE UserID = "?" OR Email = "?"

```

Note the `limit 1` as well as the quotes.

> **[best security practice for a user login from a database at DuckDuckGo](https://duckduckgo.com/?q=best%2Bsecurity%2Bpractice%2Bfor%2Ba%2Buser%2Blogin%2Bfrom%2Ba%2Bdatabase&t=vivaldi&ia=web)**
>
> DuckDuckGo. Privacy, Simplified.

---

<div class="post-metadata">

**Author:** ![2shlee](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/2shlee/32/89413_2.png) [@2shlee](https://discourse.nodered.org/u/2shlee)\
**Post date:** [25 March 2024 04:51 UTC](https://discourse.nodered.org/t/please-check-my-http-login-flow/86553/12 "2024-03-25T04:51:42Z")

</div>

There is one more area of obstruction.  
Didn't you say that the password should be hashed and stored in the database? But the bcrypt module is not available in Nordred.  
So, I found it in Palette and tried this module. (  
However, the password that was encrypted and stored at the time of membership registration and the value that encrypted the input value at the time of login do not match each other!  
I expect it to be a problem caused by different salt values, but I can't think of a solution..

---

<div class="post-metadata">

**Author:** ![zenofmud](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/zenofmud/32/316_2.png) [@zenofmud](https://discourse.nodered.org/u/zenofmud)\
**Post date:** [25 March 2024 10:07 UTC](https://discourse.nodered.org/t/please-check-my-http-login-flow/86553/13 "2024-03-25T10:07:45Z")

</div>

Closing this thread since you opened a new thread about this obstruction

> [@\[login/join\] password bcrypt with mysql](https://discourse.nodered.org/t/login-join-password-bcrypt-with-mysql/86724):
>
> When registering as a member, the password is hash encrypted and stored in the db. And when you log in, compare the "saved password value" with the encrypted input value. But the hash value does not match.. Please tell me the answer.. How can I encrypt and store my password and authenticate it? [join] [login] code [ { "id": "786b1874642b9820", "type": "bcrypt", "z": "f24308a3b5ba3065", "name": "Hash", "action": "encrypt", …

---

<div class="post-metadata">

**Author:** ![zenofmud](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/zenofmud/32/316_2.png) [@zenofmud](https://discourse.nodered.org/u/zenofmud)\
**Post date:** [25 March 2024 10:07 UTC](https://discourse.nodered.org/t/please-check-my-http-login-flow/86553/14 "2024-03-25T10:07:51Z")

</div>


