π‘οΈ Protecting IAM Users β Password Policy & MFA
Once users exist, the next job is keeping their accounts from getting compromised. Two main defenses:
π 1. Password Policy
Passwords are the first layer of defense. The stronger the policy, the harder an account is to guess, crack, brute-force, or compromise through reused credentials. A weak password can put your whole AWS environment at risk.
What you can configure:
Minimum length β set how long passwords must be
Complexity β require uppercase, lowercase, numbers, and symbols (
@,#,!)Who can change passwords β users themselves, or admins only
Expiration β force rotation on a schedule (e.g. every 90 days)
No reuse β block users from recycling old passwords
This protects against brute-force attempts, credential stuffing, and weak reused passwords. But passwords alone aren't enough.
<div class="infographic-pwpolicy">
<style>
.infographic-pwpolicy {
--ink: #1a1a1a;
--ink-soft: #5c5c5c;
--paper: #ffffff;
--paper-alt: #f6f4ef;
--accent: #2563eb;
--accent-soft:#e3ebfd;
--line: #ddd6c9;
--ok: #1e8e5a;
--ok-soft: #e2f6ec;
--bad: #c0392b;
--bad-soft: #fbe9e7;
--display-font: Georgia, 'Iowan Old Style', serif;
--mono-font: 'SF Mono', Menlo, Consolas, monospace;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
color: var(--ink);
background: var(--paper);
padding: 2rem 1.25rem 2.5rem;
max-width: 900px;
margin: 0 auto;
border-radius: 14px;
box-sizing: border-box;
}
.infographic-pwpolicy[data-theme="dark"] {
--ink: #e8e6e1;
--ink-soft: #9a968c;
--paper: #121212;
--paper-alt: #1e1e26;
--accent: #6f9bff;
--accent-soft:#1c2740;
--line: #2c313d;
--ok: #4fbf87;
--ok-soft: #143024;
--bad: #e57373;
--bad-soft: #3a2222;
}
.infographic-pwpolicy * { box-sizing: border-box; }
.pp-eyebrow { font-size: 0.75rem; letter-spacing: 0.08em; text-transform: uppercase;
color: var(--ink-soft); margin: 0 0 0.35rem; text-align: center; }
.pp-title { font-family: var(--display-font); font-size: 1.5rem; margin: 0 0 1.75rem; line-height: 1.2;
text-align: center; }
.pp-stage { display: grid; grid-template-columns: 1fr 1.15fr; gap: 2rem; align-items: center; }
@media (max-width: 700px) { .pp-stage { grid-template-columns: 1fr; } }
/* --- Policy dial panel --- */
.pp-rules { display: flex; flex-direction: column; gap: 0.6rem; }
.pp-rule { display: flex; align-items: center; justify-content: space-between; gap: 0.7rem;
border: 1px solid var(--line); border-radius: 8px; padding: 0.55rem 0.8rem; background: var(--paper-alt); }
.pp-rule .lbl { font-size: 1.1rem; font-weight: 700; font-family: var(--mono-font); color: var(--ink-soft); width: 2.4rem; }
.pp-switch { position: relative; width: 40px; height: 22px; flex-shrink: 0; }
.pp-switch input { opacity: 0; width: 100%; height: 100%; margin: 0; cursor: pointer; position: absolute; z-index: 2; }
.pp-switch .track { position: absolute; inset: 0; background: var(--line); border-radius: 999px; transition: background 0.15s; }
.pp-switch .thumb { position: absolute; top: 3px; left: 3px; width: 16px; height: 16px; border-radius: 50%;
background: var(--paper); box-shadow: 0 1px 2px rgba(0,0,0,0.25); transition: transform 0.15s; }
.pp-switch input:checked ~ .track { background: var(--accent); }
.pp-switch input:checked ~ .thumb { transform: translateX(18px); }
.pp-slider-row { border: 1px solid var(--line); border-radius: 8px; padding: 0.6rem 0.8rem;
background: var(--paper-alt); }
.pp-slider-row .top { display: flex; justify-content: space-between; align-items: baseline; margin-bottom: 0.3rem; }
.pp-slider-row .lbl { font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.05em; color: var(--ink-soft); }
.pp-slider-row .val { font-family: var(--mono-font); font-weight: 700; color: var(--accent); font-size: 0.95rem; }
.pp-slider-row input[type="range"] { width: 100%; accent-color: var(--accent); }
/* --- Live tester --- */
.pp-tester { display: flex; flex-direction: column; align-items: center; gap: 1.1rem; }
.pp-lock { width: 92px; height: 92px; }
.pp-lock .body { fill: var(--bad-soft); stroke: var(--bad); stroke-width: 3; transition: fill 0.25s, stroke 0.25s; }
.pp-lock .shackle { fill: none; stroke: var(--bad); stroke-width: 5; stroke-linecap: round; transition: stroke 0.25s, transform 0.3s; transform-origin: 46px 34px; }
.pp-lock.is-unlocked .body { fill: var(--ok-soft); stroke: var(--ok); }
.pp-lock.is-unlocked .shackle { stroke: var(--ok); transform: translateY(-6px) rotate(-18deg); }
.pp-lock .dot { fill: var(--bad); transition: fill 0.25s; }
.pp-lock.is-unlocked .dot { fill: var(--ok); }
@media (prefers-reduced-motion: reduce) { .pp-lock .shackle { transition: none; } }
.pp-input { width: 100%; max-width: 300px; font: inherit; font-family: var(--mono-font); font-size: 1rem;
padding: 0.6rem 0.8rem; border-radius: 8px; border: 1.5px solid var(--line); background: var(--paper);
color: var(--ink); text-align: center; letter-spacing: 0.05em; }
.pp-input:focus { outline: none; border-color: var(--accent); }
.pp-checks { display: flex; gap: 0.5rem; flex-wrap: wrap; justify-content: center; }
.pp-check { width: 2.2rem; height: 2.2rem; border-radius: 50%; display: flex; align-items: center; justify-content: center;
font-family: var(--mono-font); font-size: 0.85rem; font-weight: 700; border: 1.5px solid var(--line);
color: var(--ink-soft); background: var(--paper); transition: all 0.15s; }
.pp-check.pass { background: var(--ok-soft); border-color: var(--ok); color: var(--ok); }
@media (prefers-reduced-motion: reduce) {
.infographic-pwpolicy * { transition-duration: 0.001ms !important; }
}
</style>
<div class="pp-stage">
<div class="pp-rules">
<div class="pp-slider-row">
<div class="top"><span class="lbl">Min length</span><span class="val" id="pp-len-val">8</span></div>
<input type="range" id="pp-len" min="4" max="16" value="8">
</div>
<div class="pp-rule">
<span class="lbl">A-Z</span>
<label class="pp-switch"><input type="checkbox" id="pp-upper" checked><span class="track"></span><span class="thumb"></span></label>
</div>
<div class="pp-rule">
<span class="lbl">a-z</span>
<label class="pp-switch"><input type="checkbox" id="pp-lower" checked><span class="track"></span><span class="thumb"></span></label>
</div>
<div class="pp-rule">
<span class="lbl">0-9</span>
<label class="pp-switch"><input type="checkbox" id="pp-num" checked><span class="track"></span><span class="thumb"></span></label>
</div>
<div class="pp-rule">
<span class="lbl">@#!</span>
<label class="pp-switch"><input type="checkbox" id="pp-sym"><span class="track"></span><span class="thumb"></span></label>
</div>
</div>
<div class="pp-tester">
<svg class="pp-lock" id="pp-lock" viewBox="0 0 92 92">
<path class="shackle" d="M28 40 V26 a18 18 0 0 1 36 0 V40"/>
<rect class="body" x="18" y="38" width="56" height="42" rx="8"/>
<circle class="dot" cx="46" cy="58" r="5"/>
<rect class="dot" x="43" y="60" width="6" height="12" rx="2"/>
</svg>
<input type="text" class="pp-input" id="pp-input" placeholder="type a passwordβ¦" autocomplete="off" spellcheck="false">
<div class="pp-checks">
<div class="pp-check" id="pp-c-len">len</div>
<div class="pp-check" id="pp-c-upper">A-Z</div>
<div class="pp-check" id="pp-c-lower">a-z</div>
<div class="pp-check" id="pp-c-num">0-9</div>
<div class="pp-check" id="pp-c-sym">@#!</div>
</div>
</div>
</div>
<script>
(function () {
const wrapper = document.currentScript.closest('.infographic-pwpolicy')
|| document.querySelector('.infographic-pwpolicy');
const bodyAttr = document.body.getAttribute('data-darkmode');
let dark;
if (bodyAttr === 'true') { dark = true; }
else if (bodyAttr === 'false') { dark = false; }
else {
const match = document.cookie.match(/(?:^|;\s*)darkMode=([^;]+)/);
dark = match ? match[1] === 'true' : false;
}
if (dark) wrapper.setAttribute('data-theme', 'dark');
const lenSlider = wrapper.querySelector('#pp-len');
const lenVal = wrapper.querySelector('#pp-len-val');
const upperToggle = wrapper.querySelector('#pp-upper');
const lowerToggle = wrapper.querySelector('#pp-lower');
const numToggle = wrapper.querySelector('#pp-num');
const symToggle = wrapper.querySelector('#pp-sym');
const input = wrapper.querySelector('#pp-input');
const lock = wrapper.querySelector('#pp-lock');
const cLen = wrapper.querySelector('#pp-c-len');
const cUpper = wrapper.querySelector('#pp-c-upper');
const cLower = wrapper.querySelector('#pp-c-lower');
const cNum = wrapper.querySelector('#pp-c-num');
const cSym = wrapper.querySelector('#pp-c-sym');
function evaluate() {
const pw = input.value;
const minLen = Number(lenSlider.value);
lenVal.textContent = minLen;
const results = [];
const lenOk = pw.length >= minLen;
cLen.classList.toggle('pass', lenOk);
results.push(lenOk);
if (upperToggle.checked) {
const ok = /[A-Z]/.test(pw);
cUpper.classList.toggle('pass', ok);
cUpper.style.display = '';
results.push(ok);
} else {
cUpper.classList.remove('pass');
cUpper.style.display = 'none';
}
if (lowerToggle.checked) {
const ok = /[a-z]/.test(pw);
cLower.classList.toggle('pass', ok);
cLower.style.display = '';
results.push(ok);
} else {
cLower.classList.remove('pass');
cLower.style.display = 'none';
}
if (numToggle.checked) {
const ok = /[0-9]/.test(pw);
cNum.classList.toggle('pass', ok);
cNum.style.display = '';
results.push(ok);
} else {
cNum.classList.remove('pass');
cNum.style.display = 'none';
}
if (symToggle.checked) {
const ok = /[^A-Za-z0-9]/.test(pw);
cSym.classList.toggle('pass', ok);
cSym.style.display = '';
results.push(ok);
} else {
cSym.classList.remove('pass');
cSym.style.display = 'none';
}
const allPass = pw.length > 0 && results.every(Boolean);
lock.classList.toggle('is-unlocked', allPass);
}
[lenSlider, upperToggle, lowerToggle, numToggle, symToggle].forEach(el =>
el.addEventListener('input', evaluate));
input.addEventListener('input', evaluate);
evaluate();
})();
</script>
</div>.demo {
font-family: system-ui, sans-serif;
padding: 16px;
}
.demo h1 {
color: #2563eb;
margin: 0 0 8px;
}
.demo p {
color: #4b5563;
margin: 0;
}document.querySelector('.demo h1')?.addEventListener('click', () => {
alert('Hello from live preview!');
});π 2. Multi-Factor Authentication (MFA)
MFA is the stronger layer. Turn it on for the root account, admins, and anyone with elevated permissions β a compromised account with real access can delete resources or wreck infrastructure.
MFA = two things combined:
Something you know β your password
Something you have β a device
Even if Alice's password leaks, an attacker still can't log in without her device too.
<div class="infographic-mfa">
<style>
.infographic-mfa {
--ink: #1a1a1a;
--ink-soft: #5c5c5c;
--paper: #ffffff;
--paper-alt: #f6f4ef;
--accent: #2563eb;
--accent-soft:#e3ebfd;
--line: #ddd6c9;
--ok: #1e8e5a;
--ok-soft: #e2f6ec;
--bad: #c0392b;
--bad-soft: #fbe9e7;
--display-font: Georgia, 'Iowan Old Style', serif;
--mono-font: 'SF Mono', Menlo, Consolas, monospace;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
color: var(--ink);
background: var(--paper);
padding: 2rem 1.25rem 2.5rem;
max-width: 900px;
margin: 0 auto;
border-radius: 14px;
box-sizing: border-box;
}
.infographic-mfa[data-theme="dark"] {
--ink: #e8e6e1;
--ink-soft: #9a968c;
--paper: #121212;
--paper-alt: #1e1e26;
--accent: #6f9bff;
--accent-soft:#1c2740;
--line: #2c313d;
--ok: #4fbf87;
--ok-soft: #143024;
--bad: #e57373;
--bad-soft: #3a2222;
}
.infographic-mfa * { box-sizing: border-box; }
.mfa-eyebrow { font-size: 0.75rem; letter-spacing: 0.08em; text-transform: uppercase;
color: var(--ink-soft); margin: 0 0 0.35rem; text-align: center; }
.mfa-title { font-family: var(--display-font); font-size: 1.5rem; margin: 0 0 2rem; line-height: 1.2;
text-align: center; }
/* --- Login simulator --- */
.mfa-sim { display: flex; flex-direction: column; align-items: center; gap: 1.2rem; margin-bottom: 2.5rem;
padding-bottom: 2.25rem; border-bottom: 1px dashed var(--line); }
.mfa-factors { display: flex; gap: 1.5rem; align-items: center; flex-wrap: wrap; justify-content: center; }
.mfa-factor { display: flex; flex-direction: column; align-items: center; gap: 0.4rem; }
.mfa-factor .icon-box { width: 64px; height: 64px; border-radius: 12px; border: 1.5px solid var(--line);
display: flex; align-items: center; justify-content: center; font-size: 1.7rem; background: var(--paper-alt); }
.mfa-factor.always .icon-box { border-color: var(--ok); background: var(--ok-soft); }
.mfa-factor-label { font-size: 0.72rem; color: var(--ink-soft); font-weight: 600; }
.mfa-switch { position: relative; width: 46px; height: 25px; }
.mfa-switch input { opacity: 0; width: 100%; height: 100%; margin: 0; cursor: pointer; position: absolute; z-index: 2; }
.mfa-switch .track { position: absolute; inset: 0; background: var(--bad-soft); border: 1.5px solid var(--bad);
border-radius: 999px; transition: background 0.15s, border-color 0.15s; }
.mfa-switch .thumb { position: absolute; top: 2px; left: 2px; width: 19px; height: 19px; border-radius: 50%;
background: var(--paper); box-shadow: 0 1px 2px rgba(0,0,0,0.25); transition: transform 0.15s; }
.mfa-switch input:checked ~ .track { background: var(--ok-soft); border-color: var(--ok); }
.mfa-switch input:checked ~ .thumb { transform: translateX(21px); }
.mfa-plus { font-size: 1.4rem; color: var(--ink-soft); }
.mfa-lock { width: 76px; height: 76px; }
.mfa-lock .body { fill: var(--bad-soft); stroke: var(--bad); stroke-width: 3; transition: fill 0.25s, stroke 0.25s; }
.mfa-lock .shackle { fill: none; stroke: var(--bad); stroke-width: 5; stroke-linecap: round;
transition: stroke 0.25s, transform 0.3s; transform-origin: 46px 34px; }
.mfa-lock.is-unlocked .body { fill: var(--ok-soft); stroke: var(--ok); }
.mfa-lock.is-unlocked .shackle { stroke: var(--ok); transform: translateY(-6px) rotate(-18deg); }
.mfa-lock .dot { fill: var(--bad); transition: fill 0.25s; }
.mfa-lock.is-unlocked .dot { fill: var(--ok); }
.mfa-result { font-weight: 700; font-size: 0.95rem; padding: 0.35rem 0.9rem; border-radius: 999px;
border: 1.5px solid var(--bad); color: var(--bad); background: var(--bad-soft); transition: all 0.2s; }
.mfa-result.ok { border-color: var(--ok); color: var(--ok); background: var(--ok-soft); }
@media (prefers-reduced-motion: reduce) { .mfa-lock .shackle { transition: none; } }
/* --- Device picker --- */
.mfa-devices { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1rem; }
@media (max-width: 620px) { .mfa-devices { grid-template-columns: 1fr; } }
.mfa-device { border: 1.5px solid var(--line); border-radius: 12px; padding: 1.1rem 0.9rem;
background: var(--paper-alt); cursor: pointer; text-align: center; color: var(--ink);
transition: border-color 0.15s, background 0.15s; }
.mfa-device:hover { border-color: var(--accent); }
.mfa-device.is-open { border-color: var(--accent); background: var(--accent-soft); }
.mfa-device .icon { font-size: 2rem; margin-bottom: 0.5rem; }
.mfa-device .name { font-weight: 700; font-size: 0.88rem; color: var(--ink); }
.mfa-device .detail { display: none; font-size: 0.78rem; color: var(--ink-soft); margin-top: 0.5rem; }
.mfa-device.is-open .detail { display: block; }
@media (prefers-reduced-motion: reduce) {
.infographic-mfa * { transition-duration: 0.001ms !important; }
}
</style>
<div class="mfa-sim">
<div class="mfa-factors">
<div class="mfa-factor always">
<div class="icon-box">π</div>
<span class="mfa-factor-label">Password<br>(leaked)</span>
</div>
<span class="mfa-plus">+</span>
<div class="mfa-factor">
<div class="icon-box" id="mfa-device-icon">π±</div>
<label class="mfa-switch">
<input type="checkbox" id="mfa-toggle">
<span class="track"></span><span class="thumb"></span>
</label>
<span class="mfa-factor-label">Attacker has<br>the device?</span>
</div>
</div>
<svg class="mfa-lock" id="mfa-lock" viewBox="0 0 92 92">
<path class="shackle" d="M28 40 V26 a18 18 0 0 1 36 0 V40"/>
<rect class="body" x="18" y="38" width="56" height="42" rx="8"/>
<circle class="dot" cx="46" cy="58" r="5"/>
<rect class="dot" x="43" y="60" width="6" height="12" rx="2"/>
</svg>
<span class="mfa-result" id="mfa-result">β Access Denied</span>
</div>
<h2 class="mfa-title">π MFA Device Options</h2>
<div class="mfa-devices">
<button class="mfa-device" data-d="virtual">
<div class="icon">π±</div>
<div class="name">Virtual MFA</div>
<div class="detail">Google Authenticator, Authy β runs on your phone, most common choice.</div>
</button>
<button class="mfa-device" data-d="u2f">
<div class="icon">π</div>
<div class="name">U2F Key</div>
<div class="detail">Physical USB/NFC key, e.g. YubiKey β very secure, one key per user.</div>
</button>
<button class="mfa-device" data-d="fob">
<div class="icon">π«</div>
<div class="name">Hardware Fob</div>
<div class="detail">Physical token (Gemalto, SurePassID) β used in GovCloud & high-security setups.</div>
</button>
</div>
<script>
(function () {
const wrapper = document.currentScript.closest('.infographic-mfa')
|| document.querySelector('.infographic-mfa');
const bodyAttr = document.body.getAttribute('data-darkmode');
let dark;
if (bodyAttr === 'true') { dark = true; }
else if (bodyAttr === 'false') { dark = false; }
else {
const match = document.cookie.match(/(?:^|;\s*)darkMode=([^;]+)/);
dark = match ? match[1] === 'true' : false;
}
if (dark) wrapper.setAttribute('data-theme', 'dark');
const toggle = wrapper.querySelector('#mfa-toggle');
const lock = wrapper.querySelector('#mfa-lock');
const result = wrapper.querySelector('#mfa-result');
function evaluate() {
const attackerHasDevice = toggle.checked;
lock.classList.toggle('is-unlocked', attackerHasDevice);
result.classList.toggle('ok', false);
if (attackerHasDevice) {
result.textContent = 'β Access Granted β both factors compromised';
result.classList.remove('ok');
result.style.borderColor = 'var(--bad)';
result.style.color = 'var(--bad)';
result.style.background = 'var(--bad-soft)';
} else {
result.textContent = 'β Access Denied β MFA blocked it';
result.style.borderColor = 'var(--ok)';
result.style.color = 'var(--ok)';
result.style.background = 'var(--ok-soft)';
}
}
toggle.addEventListener('input', evaluate);
evaluate();
wrapper.querySelectorAll('.mfa-device').forEach(card => {
card.addEventListener('click', () => {
const wasOpen = card.classList.contains('is-open');
wrapper.querySelectorAll('.mfa-device').forEach(c => c.classList.remove('is-open'));
if (!wasOpen) card.classList.add('is-open');
});
});
})();
</script>
</div>.demo {
font-family: system-ui, sans-serif;
padding: 16px;
}
.demo h1 {
color: #2563eb;
margin: 0 0 8px;
}
.demo p {
color: #4b5563;
margin: 0;
}document.querySelector('.demo h1')?.addEventListener('click', () => {
alert('Hello from live preview!');
});Bottom line: π Password policy stops weak-credential attacks. π MFA stops attackers even when a password is stolen.