Security Points to Watch — Do Not Trust Input, Protect Secrets

This article is part of the IT Foundations course, which builds up from scratch the practical IT knowledge you need at a minimum for programming and vibe coding.
There are two basics: not trusting user input, and protecting secrets. Diagrams show how SQL injection and XSS happen.

This article covers two of the security points to watch.

One is not using the text a user typed as it is, and the other is not putting secret values where more people can read them.

  • Why input validation is done on the server
  • SQL injection, which happens when input is joined onto a statement, and placeholders
  • XSS, prevented by escaping right before output to the screen
  • Where you may keep secrets such as API keys and passwords

Once any of these happen after you publish, they cannot be undone, and data that has already been read cannot be taken back.

"No input validation" means using a value as it arrives

Input validation is checking that an incoming value is in the format, length, and range you expect before you use it.

You set a condition for each field: names up to 50 characters, star ratings from 1 to 5.

Value that arrivesCondition set in advanceWhat the server does
Name field: AliceUp to 50 characters → fitsGoes on to save it
Star rating: 7A number from 1 to 5 → does not fitRejects it without saving
Body: left emptyOne character or more → does not fitRejects it without saving

Only the first row meets the condition and goes on to be saved; the other two are not saved, and the server returns which part does not fit.

The check on the screen is run by JavaScript (a program that runs inside the browser) delivered to the user's device.

The user can turn it off, and they can also send data straight to the server without using a browser.

The same 7 takes one of two paths, depending on how it is sent
Send 7 as thestar ratingSend it fromthe formTurn off the checkand sendStopped by thecheckReaches the serverwithout the checkRejected by servervalidation
The one value on the left takes the upper or the lower path depending on how it is sent. The lower path does not go through the check on the screen, so without validation on the server it is saved as it is.

The check on the screen is there to prompt the user to fix the entry, and only the validation on the server decides whether a value may be used.

If you save without validation, values that do not meet the conditions stay in the database as they are.

Star ratings outside 1 to 5 throw off averages and sort order, and values that cannot be read as numbers stop the aggregation from running.

Fixing values that have already gone in means checking the remaining data one record at a time.

Check a value on the server before you use it

Input validation is looking at whether a value that has arrived meets the conditions you set, before you use it.

The check on the screen is there to have the user rewrite the entry on the spot and can be turned off before sending, so you check the same thing again on the server.

Join input onto a statement, and the characters typed run as a command

When it looks up saved reviews by name, the server assembles a statement in SQL (the language that passes commands to a database).

When the input text is joined onto that string, the whole thing is passed as a single string.

What is inside the single string passed to the database
The single string the database receives
The part written by the developer
  • SELECT * FROM reviews WHERE name =
  • An instruction to pull the rows whose name matches from the reviews table
  • This form is written in the code in advance
The part that came from the name field
  • Alice — an ordinary name
  • ' OR '1'='1 — text containing symbols
  • What the user typed goes into this position as it is
The outer frame is the single string passed from the server to the database. Once it has been passed, the two parts inside cannot be told apart.

Once passed, the part written by the developer and the part from the name field are parts of one and the same string.

The database reads the single string it receives as a command, all of it together.

Text entered in the name fieldStatement produced by joiningWhat the database returns
Alicename = 'Alice'Only Alice's reviews
Alice'name = 'Alice''The statement breaks and errors
' OR '1'='1name = '' OR '1'='1'Every review

Input text being executed as a command instead of as a value is injection, and the kind that happens in a database is SQL injection.

The way of writing that prevents it is a placeholder: in the statement you mark only the position where the value goes with a symbol, and pass the value separately.

The same input splits in two, depending on how the server is written
In the name field:' OR '1'='1Joined onto thestatementCommand and valuepassed separatelyReads one stringas a commandJust looks up thevalue as a nameEvery reviewis returnedEnds with 0 rows
The one input on the left takes the upper or the lower path depending on how it is written. When the command and the value are passed separately, even text containing symbols is treated as a name.

When they are passed separately, the database does not read the value as a command; it looks up that text as a name and ends with 0 rows.

Validation checks the format of a value, while a placeholder makes sure the value is treated as a value whatever its format.

They do different jobs, so you do both.

Do not join the statement and the input text into one string

When input text is joined onto a statement, even the symbols typed are read as part of the command.

Passing the command and the value separately means text containing symbols is treated as just a name, so you keep both: validation that checks the format, and the way of writing that treats the value as a value regardless of its format.

Without a change right before output, a post runs in another user's browser

The same thing happens where a post is put on the screen.

Input text being executed as JavaScript in another user's browser is XSS (cross-site scripting).

The same input gets two names, depending on what it is mixed into
Text the usertypedMixed into anSQL statementThe database readsit as a commandPass command andvalue separatelyMixed into thepage HTMLAnother user'sbrowser reads itEscape it rightbefore output
On the left is what the user typed. The upper path is where it is mixed into an SQL statement, the lower path where it is mixed into the HTML of the screen. What reads it differs, so the fix differs too.

In both, the input is read as a command; only what reads it differs.

The fix on the output side is escaping: converting characters that carry a special meaning into a form that is displayed as plain characters.

The dividing point is not when it is saved, but right before it goes on the screen.

Character with a special meaning in HTMLHow it is written after replacementCharacter seen on the screen
<&lt;<
>&gt;>
&&amp;&
"&quot;"
'&#x27;'

Replace right before output, and a post stays plain text

If you put a post on the screen as it is, whatever was mixed into it runs in another user's browser.

What stops this is the replacement made right before output, and because only the symbols are replaced, the text the reader sees does not change.

Stop secret values before more people can read them

The other point is where secrets are kept.

What you look at here is how many people can read the place you put them in.

How far the files of my-app travel
Working folder on your computer
Not recorded in Git (stays on your computer only)
  • .env — the values of API keys and passwords
  • Configuration files used only on your computer
Recorded in Git (reaches everyone you give the code to)
  • server.js — code that runs on the server
  • .gitignore — the list of files not to record
  • .env.example — a sample with only the names
Delivered to the browser (readable by everyone who opens the URL)
  • index.html — the review screen
  • script.js — code that runs on the page
The outer frame is the working folder on your computer. The further in the frame a file sits, the more people can read it, so you do not write secret values in the inner frames.

The further in the frame, the more people can read it: anyone can read script.js, and once server.js is recorded in Git, everyone you gave the code to can read it.

Writing .env in .gitignore (a file listing the names of the files Git is not to record) keeps the values out of the history, but

once they have been recorded they remain in the past history, so you regenerate the values themselves.

How far a file recorded in Git travels on GitHub
Record onlythe codePublic repositoryon GitHubAnyone in theworld can read it.env getsrecorded tooThe whole filestays in historyStill readable inthe historyWrite it in.gitignoreNever entersthe historyStays on yourcomputer only
The top row is code put in a public repository, the middle row is when .env was recorded as well, and the bottom row is when it was written in .gitignore. From left to right, where it is put determines who can read it.

A value that has gone into a public repository can be read by anyone in the world.

Where an API key is kept, and the ring of people who can read it
Readable by everyone who opens the URL
  • script.js — code delivered to the browser
  • The end of the URL you call
  • Error messages shown on the screen
Readable by everyone you give the code to
  • server.js — code recorded in Git
  • .env once recorded — stays in the history
Readable only by people who can operate the server
  • Environment variables on the server — API keys and passwords go here
The further out the ring, the more people can read it. The only places you may put a value are the ones in the innermost ring.

The only thing in the innermost ring is the environment variables on the server.

You do not write them in the places in the two outer rings, or in logs (the record of activity the server keeps), which are sometimes taken outside.

The my-app running inside the box on the right loads the value from the environment variables.

That value never goes into the two boxes on the left, so the number of people who can read it does not grow.

Where to keep it is decided by how many people can read it

Where you may keep a secret value is decided by how many people can read that place.

Anyone can read a file delivered to the browser, and a file recorded in Git reaches everyone you give the code to, so you write it in neither and put it in the environment variables on the server.

QUIZ

Knowledge Check

Answer each question one by one.

Q1Why validate on the server as well, when the form values are already checked on the screen?

Q2What does the placeholder style of writing, which prevents SQL injection, actually do?

Q3Which is the correct place to keep the API key for an external service?