Guidance on best practice: Posting to server to fetch SQL query data and redirect user back to UI with data - sql-server

Hello I am needing some assistance with a current project I am working on. The goal is to be able to interact with SQL from the UI and display the returned data in a table.
I am using node V9.8.0, express 4.16.3 and mssql 4.1.0.
I am able to connect and fetch data and display it on the UI but I do not think my set up is best practice.
My main road block was figuring out how to send information to the server to than query SQL and redirect the user back to the UI with the table displayed with the expected data.
Front end set up, form and ajax. I know that the action in the form should match the URL for ajax but to get this to work I have two post routes on the sever side. The form submits to the first post route and writes the req.body to a file. The ajax post URL reads the file and than connects to SQL and knows what data to fetch based on the user's input and predefined query strings that can take in parameters.
<form id="myForm" action="/qa-hub/tools/wss_qa_tool/sql/data" method="POST">
<div class="row">
<div class="col-md">
<div class="form-group">
<select type="text" id="slct1" class="form-control" placeholder=" SQL query search..."
name="query" list="query">
<option value="">Query select...</option>
<option value="vacant_units">vacant_units</option>
<option value="cancelable_contracts">cancelable_contracts</option>
</select>
</div>
</div>
</div>
<div class="row">
<div class="col-md">
<div class="form-group">
<input type="text" id="slct2" style="display: none;" class="form-control" placeholder="Entity #" name="location" />
</div>
</div>
</div>
<div class="row">
<div class="col-md">
<div class="form-group">
<input type="text" id="slct3" style="display: none;" class="form-control" placeholder="Unit #" name="unit" />
</div>
</div>
</div>
<button value="submit" id="submitBtn" class="btn btn-secondary" style="width: 25%; font-size: 14pt">Submit</button>
</form>
<script>
$.ajax({
type: "POST",
url: "/qa-hub/tools/wss_qa_tool/api/data",
dataType: "json"
}).then(addData)
function addData(data) {
// console.log(Object.keys(data.recordset[0]));
Object.keys(data.recordset[0]).forEach(function(column) {
$("#theadRow").append("<th>" + column + "</th>");
});
let master = "";
for(var i = 0; i < data.recordset.length; i++) {
for( var key in data.recordset[i]) {
var current = "<td>" + data.recordset[i][key] + "</td>";
master = master + current;
}
$("#tBody").append("<tr>" + master + "</tr>");
master = "";
}
$('#myTable').DataTable()
}
</script>
This is my set up on the server side. So this will take in the req.body from the form and write it to a file, than redirect the user back.
router.post("/sql/data", (req, res) => {
let query = req.body.query;
let sent_params = queries[query].params;
let sentArr = sent_params.map( x => req.body.hasOwnProperty(x));
// console.log(sentArr);
if(sentArr.includes(false) ) {
console.log("Missing parameter");
res.redirect("/qa-hub/tools/wss_qa_tool");
} else {
let tempObj = {};
for(var i = 0; i < sent_params.length; i++) {
tempObj[sent_params[i]] = req.body[sent_params[i]];
}
tempObj["query"] = query;
let sentObj = tempObj;
let userId = req.user.id;
fs.writeFile(`./temp/${userId}.json`, JSON.stringify(sentObj), (error) => {
if(error) console.log(error);
res.redirect("back");
});
}
});
This is where the magic happens. This post route will than read the file and pass in the information to saved SQL queries that can take in parameters and than sends the information back to the UI.
router.post("/api/data", (req, res, next) => {
let userId = req.user.id;
fs.readFile(`./temp/${userId}.json`, "utf-8", (err, info) => {
if(info === undefined || info === "" || Object.keys(JSON.parse(info)).length === 0 ) {
console.log("No data was found.");
} else {
let data = JSON.parse(info);
let query = data.query;
let location = data.location;
let unit = data.unit;
let selected_query = queries[query].arguments([location, unit]);
var config = {
user: 'xxxx',
password: 'xxxx',
server: 'xxxx',
database: 'xxxx'
};
sql.connect(config, function (err) {
if (err) console.log("Error at the config.");
console.log("Connected!");
// create Request object
var request = new sql.Request();
// query to the database and get the records
request.query( selected_query, function (err, queryData) {
if (err) console.log("There was an error")
// console.log(queryData);
res.send(JSON.stringify(queryData));
sql.close();
console.log("SQL connection closed.")
fs.unlink(`./temp/${userId}.json`, (err) => {
if(err) console.log(err);
console.log("File was deleted successfully!");
});
});
});
}
});
});
To me this seems like a hackish set up but I needed to produce results sooner than later. What I could not figure out or find was how can I produce the same set up with maybe one post route? I could not wrap my head around how to send information to the server, SQL query to fetch data and than redirect the data and user back to the same page. Wondering what the best practice would be for this type of set up? Or possibly any insightful reading to help guide me? Any assistance is appreciated.

Related

Advanced anti-spam measures for PHP contact form

Receiving confusing spam that shouldn't be possible with the anti-spam and form requirements I have in place, so I need help determining how to attack this particular bot.
I have a simple contact me form on my website that uses PHP to send me an email. It is run via PHP. About 3 times a day, I get an email where the form is filled out with "1", such as Name: 1, Email: 1, Message: 1. I have the email format required for my field (requiring #whatever.com), I have a Captcha AND I have a honeypot installed, but still get these emails.
My form & captcha code:
<form method="post" action="contact.php">
<label class="contenttext">Your Name:</label>
<input name="senderName" class="inputemail" required="required"><br/><br/>
<label class="contenttext">Your Email Address:</label>
<input name="senderEmail" class="inputemail" type="email" required="required"><br/><br/>
<!-- HONEYPOT --><input type="text" id="catcher" name="catcher"/>
<textarea name="message" required="required"></textarea><br/><br/>
<script src="https://www.google.com/recaptcha/api.js" async defer></script>
<div class="g-recaptcha" data-sitekey="(mysitekey)"></div>
<br/>
<div class="capfail" id="capfail">Please check the Captcha box.</div><br/>
<script>
$("form").submit(function(event) {
var recaptcha = $("#g-recaptcha-response").val();
if (recaptcha === "") {
event.preventDefault();
$("#capfail").show();
}
});
</script>
And my PHP code:
<?php
$webmaster_email = "myemail#gmail.com";
$feedback_page = "contact.html";
$error_page = "404.shtml";
$thankyou_page = "contactsuccess.html";
$senderEmail = $_REQUEST['senderEmail'] ;
$message = $_REQUEST['message'] ;
$senderName = $_REQUEST['senderName'] ;
$msg =
"First Name: " . $senderName . "\r\n" .
"Email: " . $senderEmail . "\r\n" .
"\r\n" . "Message: " . "\r\n" . $message ;
function isInjected($str) {
$injections = array('(\n+)',
'(\r+)',
'(\t+)',
'(%0A+)',
'(%0D+)',
'(%08+)',
'(%09+)'
);
$inject = join('|', $injections);
$inject = "/$inject/i";
if(preg_match($inject,$str)) {
return true;
}
else {
return false;
}
}
if (!isset($_REQUEST['senderEmail'])) {
header( "Location: $feedback_page" );
}
elseif (empty($senderName) || empty($senderEmail)) {
header( "Location: $error_page" );
}
elseif ( isInjected($senderEmail) || isInjected($senderName) || isInjected($message) ) {
header( "Location: $error_page" );
}
elseif(!empty($_POST['catcher'])) {
header( "Location: $error_page" );
}
else {
mail( "$webmaster_email", "Feedback Form Results", $msg );
header( "Location: $thankyou_page" );
}
?>
This should effectively block auto-filling bots with the honeypot, checks if someone is human with captcha, and require the fields to fit a particular format. Is there anything else I am missing, or can anyone help me understand how I would still be getting emails like this?
UPDATE:
It was pointed out that I am not using server side validation. I can set up server side, however doing that breaks my show/hide div code in my HTML form. Is there a way to submit the form, direct to the PHP file, have my PHP check if captcha is checked and if it is NOT, take user back to the HTML file (ideally with their form still filled out so nothing is lost) and display the little "Please check the Captcha box." message just like I have set up now?
You're enforcing the CAPTCHA on the client rather than the server. All it takes to defeat it is for the client to disable JavaScript. You absolutely need to do all security checks on the server.
Ok let's go, create a validation file
validation.php
<?php
$response = file_get_contents("https://www.google.com/recaptcha/api/siteverify?secret=YOUR-SECRET-KEY&response=".$captcha_data."&remoteip=".$_SERVER['REMOTE_ADDR']);
if ($response.success) {
//run your php code here
} else {
// go back to your main page or run any other code here
header( "Location: index.php" );
}
?>
so in your html
<form method="post" action="contact.php">
<label class="contenttext">Your Name:</label>
<input name="senderName" class="inputemail" required="required"><br/><br/>
<label class="contenttext">Your Email Address:</label>
<input name="senderEmail" class="inputemail" type="email" required="required"><br/><br/>
<!-- HONEYPOT --><input type="text" id="catcher" name="catcher"/>
<textarea name="message" required="required"></textarea><br/><br/>
<script src="https://www.google.com/recaptcha/api.js" async defer></script>
<div class="g-recaptcha" data-sitekey="(mysitekey)"></div>
<br/>
<?php
$response = file_get_contents("https://www.google.com/recaptcha/api/siteverify?secret=YOUR-SECRET-KEY&response=".$captcha_data."&remoteip=".$_SERVER['REMOTE_ADDR']);
if ($response.success) {
echo '<script>
$("form").submit(function(event) {
var recaptcha = $("#g-recaptcha-response").val();
if (recaptcha === "") {
event.preventDefault();
$("#capfail").show();
}
});
</script>';
} else {
echo '<div class="capfail" id="capfail">Please check the Captcha box.</div><br/>';
}
?>

How to Save Image in server

I am trying to save an image in database but when I do the following it saves with dataURL, but that URL starts with localhost, how can I prevent it? I used React for frontend.
uploadImage = (event) => {
var imgSize = event.target.files[0].size;
$('#img').attr('hidden', false);
if (imgSize < 1000000 ) {
this.setState({
image: URL.createObjectURL(event.target.files[0]),
imageSize: imgSize
});
document.getElementById("errImgUpload").innerHTML = "";
}
else {
document.getElementById("errImgUpload").innerHTML = "Maximum image size 1Mb";
}
}
<div className="form-group">
<label for="file-upload" className="custom-file-upload">
<span className="fa fa-upload"></span> Upload image
</label>
<input onChange={(event) => this.uploadImage(event)} name="file-upload" id="file-upload" type="file" accept="image/*" />
<span id="errImgUpload" className="text text-danger"></span>
</div>
The Blob is http://localhost:10002/b46e96f5-83ce-4d10-b668-2bd038721b5a, what is a blob?
URL.createObjectURL() creates a blob which is a binary representation of the file in the memory. It doesn't upload the file. I am not sure from where you got the code. You may want to read more about this at MDN.
Uploading requires a backend service to open an endpoint for you to send post data to. You need to use <input type='file'> tag to send the file as form data and set the service endpoint as the url in the form. Start with this article.
You need to post your image data with FormData, like this:
const formData = new FormData();
formData.append('image', files[0]);
fetch(url, {
method: 'POST',
body: data
}).then(...);
And blob is Binary Large Object, more detail you can find in MDN.

How to display mysql image in angularjs? (AngularJs -> Node.js -> abc.com -> mysql)

I am hosting a website on Heroku with Node.js and AngularJs but my database is somewhere else (say abc.com).
I want to store image in mysql database at abc.com (Not saving images on heroku).
I have used text, blob, longblob datatype to store image from AngularJs using ng-file-upload (npm module). When i upload image, it is stored in database.
I have created a rest api on abc.com to fetch database values and consuming rest in node.js.
Now, I want to fetch image from database and display in html page. I have fetched database value from mysql -> abc.com -> node.js -> angularjs and tried angular-base64, atob and btoa to convert database value to show image, but i had no luck.
let _arrayBufferToBase64 = function (buffer) {
return $base64.encode(buffer);
};
let _arrayBufferToBase64 = function (buffer) {
console.log('_arrayBufferToBase64')
var binary = '';
var bytes = new Uint8Array(new Buffer(buffer, 'base64'));
// var bytes = new Uint8Array(buffer);
var len = bytes.byteLength;
console.log(len);
for (var i = 0; i < len; i++) {
binary += String.fromCharCode(bytes[i]);
}
return window.btoa(binary);
};
UtilService.fetchImage()
.then(function(res){
console.log(res);
if(res.success){
let data = res.data[0].profile_pic;
console.log(data);
$scope.img = 'data:image/png;base64,'+_arrayBufferToBase64(data);
// $scope.img = 'data:image/png;base64,'+data;
console.log($scope.img);
} else {
console.log('image not found');
$scope.alt = "Image is not found";
}
})
}
template: '<img class="img-responsive img-hover" ng-src={{img}} alt={{alt}}"/>'
When my database was in heroku, above code was working fine. But now i need some help.
Thanks in advance...
Found solution for my question and want to share with others.
My requirement was to send image from angularjs to nodejs, and from nodejs to abc.com (where my database is present).
From angularjs, I used ng-file-upload as:
<div class="col-sm-12">
<button class="col button btn btn-primary btn-sm" ng-model="$ctrl.imageFile" id="imageFile" name="imageFile" ngf-pattern="'image/*'" ngf-select ngf-accept="'image/*'" ngf-max-size="2MB" ngf-resize="{width: 512, height: 512, quality: 1}">Select/Change</button>
<p class="text-danger text-center" ng-show="profileImageForm.imageFile.$error.maxSize">
2MB is max size
</p>
<p class="text-danger text-center" ng-show="profileImageForm.imageFile.$error.pattern">
Select image
</p>
<button ng-show="!!$ctrl.imageFile" class="col btn btn-primary btn-sm" ng-click="$ctrl.uploadProfilePic($ctrl.imageFile)">Upload</button>
</div>
Upload.upload({
// request method is post
url: 'server-url',
data: { imageFile: $ctrl.imageFile },
headers: Utility.authHeader
}).then(function (resp) {
// ...
})
On server side (NodeJs):
app.post('server-url', , function (req, res) {
const formidable = require('formidable');
const form = new formidable.IncomingForm();
const base64Img = require('base64-img');
form.encoding = 'utf-8';
form.parse(req, function (err, fields, file) {
logger.debug(file);
if (!!file.imageFile && !!file.imageFile.path) {
const uploadedImagePath = file.imageFile.path // imageFile is form param name
logger.debug(uploadedImagePath); // image will be stored in temp folder on server
// convert image to base64 encoded string
base64Img.base64(uploadedImagePath, function (err, base64String) {
logger.debug(base64String);
// send base64String to abc.com and store in database
});
} else {
logger.debug("Image path is not available");
res.json({ success: false })
}
});
})
When i want to display stored image:
Fetch base64String from database and use as if it is image:
Utility.fetchImage()
.then(function(res){
$ctrl.img = res.data;
})
<img class="img-responsive img-hover" ng-src={{img}} />
I hope it will help you. I would be happy to know other alternatives as well.

Firebase - issue with Auth during registration

I am new to firebase and have managed to successfully setup an authentication with email/ password based off what I have managed to gather from the documentation/ examples online. I've encountered some strange behaviour on register though. First of all here is my firebase auth code that sits in a React component function:
class SignupComponentInner extends Component {
toggleSignupLayoverAction(event){
this.props.toggleSignupLayover("false")
}
signUp(e) {
e.preventDefault();
var firstName = $('.signup-first-name').val();
var lastName = $('.signup-last-name').val();
var userName = $('.signup-user-name').val();
var password = $('.signup-user-password').val();
var email = $('.signup-email').val();
var auth = firebase.auth();
const promise = auth.createUserWithEmailAndPassword(email,password).then(function(user) {
var user = firebase.auth().currentUser;
user.updateProfile({
displayName: userName,
}).then(function() {
// Update successful.
// new db code here
firebase.database().ref('users/' + user.uid).set({
firstName: firstName,
lastName: lastName,
userName: userName
})
// end new db code here
}, function(error) {
// An error happened.
});
}, function(error) {
// Handle Errors here.
var errorCode = error.code;
var errorMessage = error.message;
// [START_EXCLUDE]
if (errorCode == 'auth/weak-password') {
alert('The password is too weak.');
} else {
console.error(error);
}
// [END_EXCLUDE]
});
promise.catch(e => console.log(e.message));
firebase.auth().onAuthStateChanged(firebaseUser => {
if(firebaseUser) {
console.log("logged in");
var sendUserId = firebaseUser.uid;
this.props.logUserIn(sendUserId)
} else {
console.log("not logged in")
}
});
}
render() {
return (
<div onClick={this.toggleSignupLayoverAction.bind(this)} className="signup-cont-par">
<div className="signup-component-inner">
<div className="signup-component-content" onClick={cancelBubble.bind(this)}>
<h2>Sign up today!</h2>
<form className="signup-form-elem">
<input placeholder="First Name" className="signup-first-name" type="text"></input>
<input placeholder="Last Name" className="signup-last-name" type="text"></input>
<input placeholder="Username" className="signup-user-name" type="text"></input>
<input placeholder="Password" className="signup-user-password" type="password"></input>
<input placeholder="Email Address" className="signup-email" type="text"></input>
<button onClick={this.signUp.bind(this)}>Sign Up</button>
</form>
</div>
</div>
</div>
)
}
}
So the registration part works exactly as it should but its the login part here that is causing issues:
firebase.auth().onAuthStateChanged(firebaseUser => {
if(firebaseUser) {
console.log("logged in");
var sendUserId = firebaseUser.uid;
this.props.logUserIn(sendUserId)
} else {
console.log("not logged in")
}
});
It's basically executing twice as im getting console.log "logged in" twice on register. Not sure why that is?
The other issue is the function that it is calling from the props this.props.logUserIn(sendUserId) This function basically hides a "create account" CTA and replaces it with a hello {{username}}. The username updates on register but very strangely it will update to whatever username I registered on the previous register! Here is the logUserIn function if it helps:
logUserIn(userId) {
var db = firebase.database();
var ref = db.ref('/users/' + userId);
console.log(userId);
ref.once('value').then((snapshot) => {
var userObject = snapshot.val();
var loggedUserName = userObject.userName;
this.setState({
LoggedInUsername:loggedUserName
})
});
this.setState({
loggedInState:true,
LoggedInId:userId,
signingUp:"inactive"
})
},
Here loggedUserNameis a state that is passed down to the main component to show the username that has just been registered (the one that is incorrectly showing the previous registered person when it should be showing the latest). Not sure where I have gone wrong.
Firebase maintains persistent connections with their servers so if you registered once, Firebase detects your browser and machine to be logged in for that newly created user. So, if you close the browser and then come back to your project on the SAME browser, Firebase still thinks you are logged in and hence, the onAuthStateChanged event handler is called.
Try it out for yourself and see what happens when you open up 2 different browsers.
To complete the whole authentication system, you need to have a logout function that will tell Firebase that this user has logged out (https://firebase.google.com/docs/auth/web/password-auth#next_steps)

Asynchronously checking if email is taken already using AngularJS

I have the following email field for taking user input for email. Once the email has been entered and focus is taken away from the field ng-blur triggers the function that checks whether the email entered by the user has already been taken:
<input type="email" class="form-control" placeholder="" ng-model="email" name="email" required ng-blur="isFound(email)">
To show the error I've got the following span:
<span class="help-block error" ng-show="blurred && isTaken">Email is taken already</span>
And here is the function isFound(email):
$scope.isFound = function(email) {
service.isEmailTaken(email).then(function(data) {
console.log(data); //this shows either null or the returned data
$scope.blurred = true;
if(data == null) {
$scope.isTaken = false;
} else {
$scope.isTaken = true;
}
});
}
Now when I try an email that has been taken, it shows the message correctly. But after that even when I enter the email that has not been taken it keeps showing the same message that the Email is taken already.
Could somebody help me understand the reason why is it happening and how to resolve it?
EDIT - Adding AngularJS and NodeJS/ExpressJS/Mongoose code below:
AngularJS
factory.isEmailTaken = function(email) {
return $http({url: "/api/queries/email/" + email, method: "GET"}).then(function(resp) {
return resp.data;
});
};
ExpressJS/NodeJS/Mongoose
app.get('/api/queries/email/:email', function (req, res) {
Query.findOne({email: req.params.email}, function (err, query) {
if (err)
res.send(err);
res.json(query);
});
});
When the email was not taken already value of data was null, and the typeof data was String.
The reason why it was not working earlier was because if(data == null) was always turning out to be false.
if(data == 'null') got things working properly.

Resources