HNHacker News
TopNewBestAskShowJobs

jkrejcha

199 karma · joined September 6, 2023

Website: https://justinekrejcha.com/

Twitter: https://twitter.com/jkrejcha/

GitHub: https://github.com/jkrejcha/

[ my public key: https://keybase.io/jkrejcha; my proof: https://keybase.io/jkrejcha/sigs/tSOZBE78TqfRY9JOzDsvZiFVbH1EvhInqlifQdJohw8 ]

submissionscomments
jkrejcha··on Google fixed more Chrome bugs in June than over the past two years, thanks to AI
I don't really agree here. Setting aside the question of how often a compiler can vectorize a loop, because I do think it's not totally relevant here, as you still don't really have the language builtin stuff that other languages do.

You really do have to write the code that iterates over a list and applies a transformation, you can't just do

    [x + y for a.x, a.y in b]
...in C, you have to iterate over the array. And you don't tend to have that much to help you. Most dialects of C don't have exceptions for instance (unless you invent your own). And in general, most of the time, non-trivial loops just... won't autovectorize and it can be a bit finnicky to nudge it in the right direction.

Take, for example, Ghidra. It has a feature to decompile blocks of code into C(++). Other reverse engineering tools follow suit. Even in C, it's very possible to model assembly instructions that don't have a 1-to-1 mapping to a particular language construct as a function call (and this is what these do).

But, there is a reason that, even with all of the architectural differences in the world, whether it be x86, x86-64, PPC, ARM, RISC-V, Xtensa, MIPS, etc, that you can provide a mapping that goes in 1 way (from source to object code) and also the other way (from object code to """source"""). It'd be much more difficult to do that with other languages, especially in an idiomatic fashion.

jkrejcha··on Google fixed more Chrome bugs in June than over the past two years, thanks to AI
C is very much a "high level/portable assembly" type language still and there's really no sign that it'll change from that. Even if it has ostensibly has abstract semantics, many operations map to 1 or a few assembly instructions and much C code, even when not strictly conforming (as all but the most trivial examples are), is conforming enough that it's portable amongst most relevant targets

For example, I kinda know that the code

    for (size_t i = 0; i < 64; i++) {
        // ...
    }
...will map to something that roughly that, on x86 will (in most cases) probably do a compare and jump, etc, for the loop body, vectorization and some other optimizations notwithstanding. I know a similar thing will happen on PPC, ARM, etc.

This relationship breaks down a bit with a lot of higher level languages where what you want to occur is higher level. This can be both a blessing and a curse; it can enable easier opportunities for optimization but may also may make automatic things downright pessimizied with very very very large effort and expenditure to get things close to usable.

jkrejcha··on Hackers shoveled snow for company, were rewarded with network admin access
"That's the best password ever!"
jkrejcha··on Hackers shoveled snow for company, were rewarded with network admin access
Good security policies should have an upper bound on password length, but also those upper bounds should maybe be like 100 characters or so. There's a couple reasons for this. First being is that hashing does take some compute resources, second being that there is some security/usability tradeoffs here, etc, and third being that after like 30 characters or so, the effective (key phrase here) security gains become marginal.
jkrejcha··on FreeBSD ate my RAM
Ostensibly you could subtract "Shmem"[1] in /proc/meminfo from the cached value... maybe?

Do agree it's not the best UX and utilities should probably do a better job at showing that

[1]: https://man7.org/linux/man-pages/man5/proc_meminfo.5.html

jkrejcha··on FreeBSD ate my RAM
This is only really a problem if you accept overcommit as a force of nature that can't be changed or tweaked (you can still do address space reservation without needing overcommit)

If you don't, it becomes rather easy (and strict commit accounting is done for example on Linux even if it isn't used in some cases)

Memory mapped files can be entirely recreated from the disk so no need to charge for them. Anonymous pages (whether private or shareable) have to be charged. Shareable memory is the harder one to charge. (The case where a mapping is used by only one can get charged as private commit.) These two previous cases are charged even if in a swap file or whatnot

jkrejcha··on FreeBSD ate my RAM
> The “cached” memory includes tmpfs and ramfs for seemingly no reason.

If you're curious why that is by the way, it's because that's actually how these are implemented (tmpfs/ramfs is just a mount to a filesystem where the files never get marked clean[1])

[1]: https://www.kernel.org/doc/Documentation/filesystems/ramfs-r...

jkrejcha··on Anonymous GitHub account mass-dropping undisclosed 0-days
> ...Ghidra

Ah, MS07-052[1] ("code execution leads to code execution") strikes again. These sorts of "if you allow arbitrary code to be executed, code will be executed" "vulnerability" reports seem evergreen

[1]: https://devblogs.microsoft.com/oldnewthing/20070807-00/?p=25...

jkrejcha··on We all depend on open source. We will defend it together
> a version of SQLite that has a severe vulnerability

Calling CVE-2025-70873 a severe vulnerability is a bit overplaying it imo. The vulnerability requires that you allow an attacker to import an arbitrary ZIP file

I looked at the vulnerability in question by the way, CVE-2025-70873, and it really is not that severe unless you're allowing users to import arbitrary ZIP files

jkrejcha··on Stop Using JWTs
It simply doesn't mitigate CSRF by itself
jkrejcha··on Stop Using JWTs
In the Discord case they can just call the `getToken()` function. It's not on `window`, but it's trivial to find.

The mitigation is rough on systems where SIGKILL happens early and often. Presumably this is why Discord doesn't do this on mobile. You switching out of the app has a much higher likelihood of that happening than on other platforms. You can't rely on onunload ever getting a chance to run

This also has barely anything to do with local storage, you could do the same for cookies. But with cookies, the browser blocks JS from getting your tokens at all if you use HttpOnly and don't leak it in responses or whatnot, so you don't need to (though you certainly can delete window.cookies if you want as well)

jkrejcha··on Developers don't understand CORS (2019)
Gonna play devil's advocate here

I think to some extent CORS and SOP are a bit equivocated somewhat intentionally... i.e. CORS is a used as a shorthand for SOP because CORS is a more well known term, especially in its acronymized form.

Consider for example the error I get when opening up the console on Google's homepage

> Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://play.google.com/log?format=json&hasfast=true. (Reason: CORS request did not succeed). Status code: (null).

There is a reference to the SOP in here but it starts off with "Cross-Origin Request" and also says "CORS request did not succeed". Which makes it sound like CORS is the problem or the thing doing the blocking or that "CORS" is enforcing the "Same Origin Policy". Chrome, I've heard, is even more cryptic

Also, it feels like one of those things that are a little pedantic to mention in a discussion irl, so that's probably why the term CORS sticks and the term SOP doesn't

jkrejcha··on Linux eliminates the strncpy API after six years of work, 360 patches
With an variable length array of structs, you can set all the fields all to 0 at the cost of an extra member at the end. In the cases where this is, the structures are such that (either intentionally or by consequence) something with all fields zero is outside of the function's domain

A little bit related: https://devblogs.microsoft.com/oldnewthing/20091008-00/?p=16...

jkrejcha··on Linux eliminates the strncpy API after six years of work, 360 patches
Reminds me of an article[1] that described how he cut GTA Online loading times by 70% because strlen was getting called for effectively every character in a string

[1]: https://nee.lv/2021/02/28/How-I-cut-GTA-Online-loading-times...

jkrejcha··on Stop Using JWTs
> True. However it's not impossible to mitigate that: https://news.ycombinator.com/item?id=48563286

It's not a perfect mitigation for session stealing, isn't available in all cases, requires custom code to implement, and also can in some cases completely break sessions (unless they have another place where it's stored)

> It's a separate attack vector. It's easily mitigated by having a one week back-off before upgrading. (and as I have said already, also affects binary executables)

A separate attack vector for the same problem. Which is also not mitigated by dependency back-off. If you load a 3rd party script into your site, you're relying on that 3rd party not getting compromised.

For example, if I have a page

<script src="https://example.com/accel.js" />

and if "accel.js" gets maliciously replaced, it can read all of the data out of local storage. No updates on your end required.

jkrejcha··on Linux eliminates the strncpy API after six years of work, 360 patches
The problem here is that null kinda is consequential of intentional design of the type system itself. In this way, I do think that null was discovered, rather than invented. Remember, C is a kinda "portable assembler" so the constructs in it are based relatively closely to how low level data structures are mapped out in memory.

This is, and continues to be, an incredibly useful feature that makes C and C structs immensely useful concepts. Part of that does need an invalid value[1]. NULL is convenient for this and although there are some very weird JavaScript-trinity-meme-style consequences for this[2], it's such a useful concept that basically all languages that have the ability to construct pointers have a null pointer[3].

The alternative world looks like everyone inventing their own invalid values. Invalid, non-null, pointers are typically MUCH worse than null pointers for debuggability and security. If you unintentionally read/write/execute memory at 0x0 (by far the most common value for NULL), most operating systems will trap this, whereas may not necessarily if 0x12345678 is your invalid value.

[1]: Stuff like IA64 had NaT bits which were effectively an extra bit for what I assume to be this sorta thing. The problem with this is that it costs an extra bit. I don't really know much about IA64, but presumably [NaT 1] + [don't care] would be your null pointers here. I think?

[2]: Really what the standard, in my opinion, should have done is probably not make use of the null pointer UB for many different functions. A lot of compilers took the UB surrounding that to make incredibly dubious "optimizations" that broke stuff with zero actual performance benefit whatsoever

[3]: Yes, even Rust. Although some (again in my opinion) unfortunate design decisions made it so that C-Rust FFI isn't zero cost because of how it treats spans/slices

jkrejcha··on Stop Using JWTs
If you exclude all of the flaws of one implementation and include all of the flaws of another, then of course you can say one is better than the other. But that's not a fair comparison of implementations

CSRF has incredibly simple mitigations with cookies (SameSite=strict) and can even work across different path segments (although this tends to be uncommon) and has mitigations against XSS which local storage does not. XSS is bad in either case (CSPs aren't usually an effective mitigation for a variety of reasons[1]) but being able to outright steal the session token is worse than being able to use it in a potentially more limited context

[1]: Primarily that they're generally just not implemented and that a lot of web frameworks require incredibly loose CSPs up to and including unsafe-eval since they often need to dynamically load JS. In addition, this doesn't protect from supply chain attacks (JS that is injected as the result of it being loaded from another domain, like from a CDN for example).

jkrejcha··on Stop Using JWTs
The only difference between the cookie scenario and localstorage scenario here is that you're reading "storage/default/<domain>/ls/data.sqlite"

It's even conveniently grouped by domain

There's no magic hardening going on with local storage (session storage is the same here), it's still SQLite

Even if so, calling ReadProcessMemory/process_vm_readv on another process really doesn't raise alarms that significantly because there are a lot of legitimate programs that do so

jkrejcha··on Stop Using JWTs
> First time I'm hearing that frameworks require disabling HttpOnly.

They effectively do in the case where you're using local storage because they need to grab the session token from somewhere. The thing about HttpOnly is that JS code never even gets to see the session token. Which is a mitigation for a whole class of vulnerabilities.

> ...CSRF

You have to mitigate CSRF server-side (with a CSRF token, checking the Sec-Fetch-* headers) or by using SameSite on the client side (ideally both)

There's a reason "don't use local storage for security sensitive stuff" is part of the OWASP cheatsheet[1]

[1]: https://cheatsheetseries.owasp.org/cheatsheets/HTML5_Securit...

jkrejcha··on Stop Using JWTs
For those curious, here are the relevant specified fields along with the relevant standards (both IETF, OIDC, etc)

JWT: https://www.iana.org/assignments/jwt/jwt.xhtml

JOSE: https://www.iana.org/assignments/jwt/jwt.xhtml

jkrejcha··on Stop Using JWTs
> - HttpOnly fights XSS which is impossible to execute with modern frontend frameworks.

Eh. Frontend frameworks tend to make successful XSS much worse because they tend to require disabling HttpOnly for not very good reasons. HttpOnly is a nice defense in depth measure against the consequences of XSS.

> - SameSite fights CSRF but the real solution is to disable loading the website in iframes (remember clickjacking?).

Disabling iframes doesn't fix CSRF. You can still <form method="..." /> or <img /> tags or whatever. For an example, see these universal logout pages. SameSite helps with CSRF (you really should also using CSRF tokens as the primary control and maybe using the Sec-Fetch-X headers as well).

jkrejcha··on Stop Using JWTs
If the attacker can already execute code as you unrestricted, then you've kinda already lost.

Local storage isn't any better in this regard

jkrejcha··on Stop Using JWTs
JOSE can still have problems if it's secure when implemented properly. A lot of API surfaces for them can kinda suck. If secure when held right was equivalent to good, then that would apply also to stuff like X.509

There are better alternatives for a lot of cases, standard session tokens or API keys are a popular one in use in most major websites online and work pretty much perfectly for most use cases.

I'm not gonna say those standards are completely without merit. The best thing about them is that it is some basic standard on passing stuff around that isn't like ASN.1 encoded or whatever, to which the tooling seems incredibly brittle and bug-prone.

jkrejcha··on Stop Using JWTs
A lot of times local storage is much less secure than using cookies. Cookies have about 20 years of infra built around it (HttpOnly, SameSite, Secure, etc). There's some weird parts about cookies, but local storage really shouldn't be used for anything security sensitive
jkrejcha··on Reviews have become expensive, rewrites have become cheap
For those curious what CL is (I had to look it up myself): it means change list[1]. Guessed from context that it was like "diff in PR/MR" or something but hadn't seen this acronym before

[1]: https://stackoverflow.com/a/27520705/2805120

jkrejcha··on The time the x86 emulator team found code so bad they fixed it during emulation
Apparently people did this with the DirectX "3D Tunnel" demo as well[1] back over 20 years ago.

Also there was one "that checked if you were printing a specific string used by a popular benchmark program. If so, then it only drew the string a quarter of the time and merely returned without doing anything the other three quarters of the time".

[1]: https://devblogs.microsoft.com/oldnewthing/20040305-00/?p=40...

jkrejcha··on Solar generates more energy in US than coal for first time
Worth noting that in the US mains is split phase so we get in 240 V with 2 supply lines (large appliances can use 240 V) that give us 2 120 V lines
jkrejcha··on CSS: Unavoidable Bad Parts
Eh. HTML in its recent iterations made it very clear to try and separate itself from layout concerns. It's why <font> was deprecated.

With that: <table> is really only meant for actual tabular data and not about layout. It really isn't about the layout. It got abused for layout because it was the only thing that worked in a lot of cases (especially amusingly enough, email)

<b> and <i> I think I'd agree, even if they did eventually get retconned to be "bring attention to" and "idiomatic text". Same with <strong> and friends. This happened with other elements too.

I think it's fair to acknowledge the realities (people who are writing HTML a lot of times use <b> to mean "bold") of usages but ideally the semantic meanings are useful. I have seen cases where "<strong>" emphasizes it by doing something other than bold for example

jkrejcha··on Raspberry Pi 5 – 16GB RAM
USB A? That's very much not legacy

If anything, it's probably like an order of magnitude more common, even for new designs

jkrejcha··on Raspberry Pi 5 – 16GB RAM
Pis/other SBCs and ESPs/STMs/etc are effectively a different subclass of embedded

Firstly, Pis and SBCs like it tend to be fully functional OOBE on their own. This has useful properties but also means that hooking them up and testing their use is a little bit simpler even compared to a microcontroller dev board

Secondly, it's a full Linux ecosystem and all of that that it entails. This ecosystem has more in it than these ecosystems (especially in the FOSS world) and it's also useful for projects that exceed a few MB of RAM. Sure if I want to do a few things a microcontroller like this is a very very good and probably the better option (it isn't that difficult to write a lil C to control these things) but SBCs can do lot more than that while still keeping many of the advantages, which may be the difference in some cases

Page 1 of 4Next →