[HN Gopher] Escaping user input is ridonkulously hard
___________________________________________________________________
Escaping user input is ridonkulously hard
Author : yimby
Score : 55 points
Date : 2022-10-31 07:14 UTC (1 days ago)
(HTM) web link (codeofhonor.substack.com)
(TXT) w3m dump (codeofhonor.substack.com)
| 6510 wrote:
| It is exactly as hard as one would expect it to be if the only
| document format becomes an application platform but you still
| want to do documents.
| js2 wrote:
| The interesting part of the article is below the fold and not
| reflected in the headline:
|
| https://codeofhonor.substack.com/i/78789944/security-theater...
|
| "Most of the rendering bugs I've seen in security audits don't
| matter. This is not how your organization will be pwned. ... What
| would fix this? Layered security built around a plausible threat
| model. What would not help? Removing reflected ASCII text from
| Shodan's API error message. I'm not saying that small security
| bugs aren't worth fixing, or that organizational security always
| trumps application security. Rather, real damage usually does not
| come from where security engineers tend to expect, because they
| spend their time on pentests and CTFs that differ substantially
| from the approaches popular among actual attackers."
|
| Everyone commenting that dealing with user input is easy: if it
| were really easy, we wouldn't keep making the same mistakes. I
| fixed my first SQL injection attack by switching some code to
| bind variables over 20 years ago, yet we still have Little Bobby
| Tables showing up in our collective databases. The fix may be
| easy ("just do X"), but the mistake is even easier.
| AtlasBarfed wrote:
| Breadth-first security attacks will exploit input sanitizing
| exploits like that. Security audits can certainly help with
| that, assuming they don't impose a huge security infrastructure
| and review process that crushes developer productivity, which
| always seems to happen.
|
| Depth-first attacks as described are a different class of
| attack, and of course "audit" won't help that much. Education,
| penetration testing, and honeypots are some of the stuff that
| works for that.
|
| Ultimately, if an organization treats its work force like crap,
| then depth-first attacks are unstoppable. The crypto-locker
| attackers are strangely pro-worker, because it highlights how
| disgruntled employees are such effective attack vectors via
| bribery, vengeance, or apathy.
| iLoveOncall wrote:
| After reading the article I fail to see what is hard about
| escaping user input.
|
| It seems like what the author means is that it's hard to think of
| all the places where user input should be escaped, but even then,
| if you use any modern framework, everything is escaped by
| default.
| AtlasBarfed wrote:
| Bobby tables: that is sanitizing inputs, not escaping. related,
| but not the same.
| kayodelycaon wrote:
| Ruby on Rails pretty much handles this. Regular strings are
| always escaped in views. Only html_safe strings will emit html.
| For user input, you should always use the sanitize method instead
| of raw. :)
| phyzome wrote:
| It's not, though. It's the easiest thing in the world: Just use a
| library that never emits unescaped content by default, or if you
| make a single-character typo.
|
| The problem is that most of the libraries aren't that.
| Joker_vD wrote:
| Different contexts require different escaping schemes, you
| know?
| masklinn wrote:
| > It's the easiest thing in the world: Just use a library that
| never emits unescaped content by default
|
| That doesn't make any sense? Escaping is a function of the
| consumer, not the producer. Hell, most of the problematic
| content doesn't come from a library to start with.
|
| And if your Markdown -> HTML converter produces escaped
| content... it's not a Markdown -> HTML converter, because the
| result is not HTML.
|
| More broadly, I think one of the core issues is this:
|
| > Escape user input
|
| User input is a broad and complicated category, and it's easy
| for user input to be "laundered" as it moves through an
| application.
|
| And then escaping is an explicit action, which means it can be
| missed or forgotten, which is also a problem.
|
| This means the solution is really that APIs should default to
| escaping most everything. Rather than having to mark
| "untrusted" content, it's _trusted_ content which should be
| marked thus. "Escaping" is the wrong default.
|
| But of course that doesn't solve all the issues. Like markdown,
| where you want the output of the Markdown converter to be
| trusted (otherwise the output won't be properly formatted on
| display), what you don't want trusted is _the input_ , and that
| means you don't want the input to be laundered through the
| Markdown converter.
|
| Which is an issue in most Markdown libraries, as they inherit
| the "trusted input" model from Gruber's original Markdown,
| where HTML passthrough was a feature.
|
| In that sense one design I did enjoy is Jinja and Markupsafe in
| the Python ecosystem:
|
| - Like most modern template libraries, Jinja escapes content by
| default.
|
| - Also (though somewhat sadly) like most template libraries
| Jinja allows marking a value as safe at point-of-use, however
| that's dangerous as content can be mixed and it's easy for safe
| content to suddenly be swapped out for user input and become
| unsafe through seemingly unrelated changes.
|
| - So a better method is to use `markupsafe.Markup` at the
| source, it's a string subclass which the library considers safe
| (because Jinja uses `markupsafe.escape` internally), the neat
| thing is _any combination between a Markup instance and a non-
| Markup string will implicitly escape the non-Markup
| parameter(s)_.
|
| This means you can mark safe content as safe _at the source_
| (where it 's easy to prove it's safe because e.g. it's a
| literal), then most transformations will maintain the safety
| invariants. Though obviously it only works with content you
| know will ultimately be markup-injected.
|
| And non-method APIs can't be overridden (e.g. re, or HTML/XML
| libraries) so they're not Markup-aware, they'll treat Markup
| objects as regular strings which that complicates processing
| pipelines if you want to conserve safety invariants. At the
| same time, those are laundering opportunities so care is
| useful.
| phyzome wrote:
| I can give a more detailed response later, but...
|
| << Escaping is a function of the consumer, not the producer
| >>
|
| This is incorrect. The producer emits something in a
| _language_ , be it HTML or JSON or HTTP headers or whatever.
| Data must be encoded properly for that language. The consumer
| must then decode, of course, so in a sense it is the job of
| both. But the onus is really on the producer.
| masklinn wrote:
| > This is incorrect. The producer emits something in a
| language, be it HTML or JSON or HTTP headers or whatever.
| Data must be encoded properly for that language.
|
| Which is the consumption side. When you send data to an
| HTML template engine, it's escaped as input, meaning with
| the template engine as consumer, not with the template
| engine as producer.
|
| It may be a "pipeline" situation where the consumer also
| produces something (e.g. JSON or HTML), but it doesn't have
| to be e.g. an SQL interface might have no production, but
| the data it consumes still needs to be properly escaped.
|
| When your producer produces data, it has no idea how that
| data will be used, and that's what determines the necessary
| transformations e.g. it's of no help to you if your
| templating engine generates content escaped for MSSQL when
| you're not going to put it in MSSQL.
| cesarb wrote:
| > it's of no help to you if your templating engine
| generates content escaped for MSSQL when you're not going
| to put it in MSSQL.
|
| Allow me to complain a bit about MSSQL.
|
| When you're escaping a LIKE expression for MSSQL, you
| _must_ also escape the "[" character, since it's a
| wildcard for MSSQL (and nowhere else except AFAIK
| Sybase). When you're escaping a LIKE expression for other
| databases, you must _not_ escape the "[" character,
| since some databases reject escaping anything other than
| the % and _ wildcards. That is, your escaping code for a
| LIKE expression has to be database-specific, because
| MSSQL (and AFAIK Sybase, it seems both have a common
| ancestor) decided to be different.
| masklinn wrote:
| > When you're escaping a LIKE expression for other
| databases, you must not escape the "[" character, since
| some databases reject escaping anything other than the %
| and _ wildcards. That is, your escaping code for a LIKE
| expression has to be database-specific, because MSSQL
| (and AFAIK Sybase, it seems both have a common ancestor)
| decided to be different.
|
| TBF you may need custom codepaths because defaults
| diverge as well, IIRC postgres and sqlite default to
| ESCAPE '\' while mssql and oracle default to ESCAPE ''
| (the latter being the actual spec behaviour).
|
| So in Postgres and SQLite you must always escape your
| LIKE parameter, while in mssql and oracle that's not the
| case.
| cesarb wrote:
| > TBF you may need custom codepaths because defaults
| diverge as well, IIRC postgres and sqlite default to
| ESCAPE '\' while mssql and oracle default to ESCAPE ''
| (the latter being the actual spec behaviour).
|
| The trick is to just avoid the default, and always use an
| explicit ESCAPE, which should work the same on every
| database (except mysql without NO_BACKSLASH_ESCAPES in
| which you also have to escape the backslash itself,
| otherwise it will escape the closing quote and get very
| confused, but that issue can be avoided by using a
| character other than backslash as the escape character).
| buttocks wrote:
| The whole point is that the producer may be hostile, or
| buggy, and the consumer must handle that. Asserting that it
| "must" be encoded properly does not make it so.
| nl wrote:
| Too bad if your consumer has to interact with anything that
| could be malicious in any circumstances!
|
| Consumers must properly escape any input.
| deredede wrote:
| That doesn't make sense to me and I agree with GP. If I
| consume HTML and I escape all HTML input I'm given, I'm
| utterly useless.
|
| Now when I consume _text_ and convert that text into HTML
| for further treatment, I 'm producing HTML, and I must
| properly escape my input in that conversion. The escaping
| is only needed because I produce HTML. In fact the only
| time escaping can be done is when producing data, because
| if unescaped data is ever produced, the cat's out of the
| bag.
|
| Edit: Actually think that producer/consumer is a wrong
| way to talk about this. Escaping only ever occurs at a
| boundary when transforming between formats (eg from "text
| string" to "html string") which is always both producer
| (of the new format) and consumer (of the old format). But
| it can always be thought of as a type cast, with possible
| type confusions when input and output formats share the
| same machine representation (eg string).
| masklinn wrote:
| > That doesn't make sense to me and I agree with GP. If I
| consume HTML and I escape all HTML input I'm given, I'm
| utterly useless. [...] Now when I consume text and
| convert that text into HTML for further treatment, I'm
| producing HTML, and I must properly escape my input in
| that conversion.
|
| Which is my point, it's the _consumption_ side which
| defines what the escaping should be.
|
| > Escaping only ever occurs at a boundary when
| transforming between formats (eg from "text string" to
| "html string") which is always both producer (of the new
| format) and consumer (of the old format).
|
| A database interface is not a transformer / producer,
| needs escaping. Globbing is not a transformer either.
| Still needs escaping.
| deredede wrote:
| I disagree, a database interface is a format boundary at
| which a transformation occurs (from text to SQL) and so
| is globbing (from text to pattern).
| masklinn wrote:
| Whatever, call it a transformer if that makes you hard.
|
| Point doesn't change: what escaping is needed is a
| function of the "transformer" and applied to the input (=
| consumption) side of it.
|
| You don't apply an escaping because data comes from a
| database, you apply it because it goes into one. Same
| with template processors, regex engines, etc...
| nl wrote:
| Sure, redefine the terms if you like.
|
| The thing that accepts the input _must_ make sure it is
| properly escaped. Think of SQL injection attacks - they
| are because the thing that accepts input hasn 't properly
| escaped the input.
|
| Cross site scripting attacks are exactly the same thing
| but occur when the input side doesn't properly escape
| HTML input.
| rileymat2 wrote:
| I suspect you are having a vigorous debate about the ill
| defined "producer consumer" terminology and probably
| agree.
| neon_electro wrote:
| Is Rails' `html_safe` an example of what you're referring to?
| https://apidock.com/rails/String/html_safe
| mrkeen wrote:
| Escaping isn't always a yes/no question.
|
| If someone enters "foo bar" into your frontend, should the
| backend only see "foo%20bar" ?
| pavlov wrote:
| The back-end should see a 7-byte buffer with values [102 111
| 111 032 098 097 114], assume it's UTF-8 and convert that to
| its internal string representation?
| ezfe wrote:
| no, the backend has no reason to see `foo%20bar` - you escape
| when you're combining that string with other strings (ie into
| HTML, into a SQL query, etc.)
| evilDagmar wrote:
| There's just a whole library of CVEs for people who attempt
| to escape things being sent to SQL. Use parameterized
| queries already.
| shadowgovt wrote:
| This is a space where type systems can be extremely helpful.
|
| Escaped input and unescaped input are separate types. And a
| robust type system will allow you to craft your functions so that
| the streams cannot be crossed without going through translation
| layers.
|
| In fact, the most robust type systems will offer things like
| automatic function composition so that you have to write a
| minimum of code... If a type coercion function is available, the
| type system can be taught to just automatically apply that
| coercion function before dropping the string into the relevant
| processing.
| evilDagmar wrote:
| I've always found it more useful to just discard user input that
| doesn't come in the format you're asking for, and bail on the
| entire operation.
|
| Like, if the user might be _attempting_ something fishy, there 's
| no reason to try and "clean it up" and have your program "do it's
| best" with the remainder. Throw an error back at the user and
| move on to the next query.
| OkayPhysicist wrote:
| This is the way. Parse, don't sanitize, and fail early. If 99%
| of traffic follows the pretty path, and most of the rest is
| actively hostile.
| teddyh wrote:
| An alternate view: "string" is not a granular enough type, just
| like "bitfield" is not a type. Firstly, a string could be raw
| unknown bytes, verified UTF-8, or UCS-2 (or even UTF-16 or
| UCS-4), and you _absolutely_ need to know which it is. But let's
| assume that you've been a diligent programmer and filtered all
| that at the edges, and now have a sequence of Unicode code points
| (or possibly graphemes). You still need to know the escaped-ness
| of the string! _This is also a form of typing_. Perl was early
| with its concept of "tainted" strings, but modern languages can
| use types to mark this concept in the code. At all points in your
| code, you should be sure what type the value you have is. If you
| need to use the types in your language to ensure this, then use
| types. But make sure of it _somehow_.
| bcrosby95 wrote:
| Escape strings at the last possible moment, and ideally it's
| done by whatever library you're using so you never have to
| worry about it. It's never not been clear to me in our
| codebases if I'm dealing with a raw string or a safe one.
| They're all unsafe, because you have no clue what context
| they're going to be used in.
|
| If you're writing a web framework or a DB library things might
| be different though - in that case a different class probably
| makes sense. If you have a module for a certain communication
| medium, then yeah you might use it _in that module_. But if you
| 're writing a webapp, passing around escaped strings is a bad
| idea 99% of the time. It creates code highly coupled to one
| aspect of your system.
|
| Just imagine if you did this with networking. I'm glad we're
| not in a world where we're passing around TCPString or
| UDPString or IPString or EthernetString or TokenRingString or
| CarrierPigeonString because that happens to be a networking
| stack the app uses sometimes. It sounds like hell.
| masklinn wrote:
| > They're all unsafe, because you have no clue what context
| they're going to be used in.
|
| That's correct, but it's the reverse thinking from the
| escaping one.
|
| Because in the escaping one, when you need not to escape you
| will also not-escape at the last possible moment, and that's
| a sure-fire way to launder attacker-controlled data.
|
| Instead you should escape everything, and _opt-out_ as early
| as possible.
|
| > But if you're writing a webapp, passing around escaped
| strings is a bad idea 99% of the time. It creates code highly
| coupled to one aspect of your system.
|
| That's why you do the reverse: most strings are unsafe to
| everything, but the strings which are safe are generally safe
| to one specific subsystem. So you say _that_.
|
| > Just imagine if you did this with networking. I'm glad
| we're not in a world where we're passing around TCPString or
| UDPString or IPString or EthernetString or TokenRingString or
| CarrierPigeonString because that happens to be a networking
| stack the app uses sometimes. It sounds like hell.
|
| It sounds like hell because it makes no sense, there's no
| such thing as a TCPString because TCP is not string-based and
| TCP messages are not composed that way.
| [deleted]
| teddyh wrote:
| > _Instead you should escape everything, and opt-out as
| early as possible._
|
| That's not even remotely workable for any system with more
| than one kind of "escaping". What if I want to use a string
| as:
|
| 1. An IDNA-encoded domain name
|
| 2. An HTML text snippet
|
| 3. A shell command string argument
|
| 4. A string literal part of a regular expression
|
| 5. A part to be used in an XML CDATA section
|
| 6. A JSON string
|
| I _can't_ escape the string beforehand, since the escaping
| rules are all different. No, the only sensible alternative
| is to use the same rule which we all use for character
| encoding: Encode and decode (and escape) _at the edges_.
| masklinn wrote:
| > I can't escape the string beforehand, since the
| escaping rules are all different.
|
| You're still misunderstanding. You shouldn't escape at
| any point, instead you should _mark things as safe_ as
| early as possible.
|
| "Safe" almost always has a single context, you don't care
| if it's going to go somewhere else because it's not safe
| for there.
|
| Anything that's not marked as safe is then automatically
| considered unsafe and processed as such by the sink.
|
| > What if I want to use a string as:
|
| It's not an issue, because by default nothing is safe
| anywhere, so all those APIs should treat the injected
| data thus.
|
| There is no escaping, because everything is automatically
| internally escaped by default.
| tialaramex wrote:
| > Firstly, a string could be raw unknown bytes, verified UTF-8,
| or UCS-2 (or even UTF-16 or UCS-4), and you absolutely need to
| know which it is.
|
| This is a language defect. If your language was invented in the
| 1960s it's an _understandable_ defect, but it 's still a
| defect. I do not want to write computer software with strings
| in a language that doesn't even have an actual string type
| rather than "Eh, maybe this is a string or maybe it's just some
| random bytes, who cares".
|
| Only in very low level software should it make a difference
| whether the string is in fact represented as UTF-8 or UTF-16 or
| whatever, but Rust shows that you can write software at a low
| level and _still_ enforce type safety for strings.
|
| I agree though that here once again the Right Thing(tm) is a
| strong type system. If I've got a Microsoft Graph username, a
| URL, an email address and a UUID, that's four types, those are
| not four strings with human names to distinguish them. We don't
| need to escape some or any of these types - in their context.
| FreezerburnV wrote:
| A type system isn't going to save you from users submitting
| all kinds of potentially different encodings. Which also
| depends on what kind of user input is being handled: Is it
| OS-provided UI? Is it something being sent to a service
| accessible on the internet? Is it from a CLI? Is it from a
| file? Context matters for the potential space of what kind of
| data you might be operating on, which could require different
| ways of either knowing what kind of data you have based on
| having more control over the input versus having to detect
| stuff (or be told, correctly) from highly arbitrary things
| like reading from a file. All of that is external to the type
| system, and requires doing something before you can tag it
| with the correct type. Some languages might attempt to detect
| this stuff for you, but that could potentially be considered
| a language defect if it's hard to detect what a string is
| without having other input telling you what that string
| contains, such as a header in an HTTP request saying that
| it's UTF-8.
| jerf wrote:
| "A type system isn't going to save you from users
| submitting all kinds of potentially different encodings."
|
| Yes, it is, because you give that a type that indicates you
| don't know what the encoding is, like RawInput or
| something. You then can not pass this type to any other
| function that doesn't explicitly call for that type. If you
| have some function that accepts it, blindly casts it to
| UTF-8, and slams it out into a file, well, that's not the
| type system's fault [1].
|
| Of course a type system won't prevent you from still just
| being wrong or writing bugs; nobody promises that, not even
| the formal methods advocates. But it _will_ prevent you
| from just accidentally blindly shoveling it out somewhere
| it doesn 't belong without ever examining it or thinking
| about it.
|
| I think you may be believing in a popular myth about strong
| typing systems, that they are designed to somehow prevent
| bad data from coming in to your system at all. You
| correctly identify that as impossible. But what strong
| typing systems _can_ do is force you to _deal_ with the
| fact that bad data may be coming in. On the outside, you
| have the chaos of, say, a bag of bytes that may or may not
| be JSON. On the inside, you have a "type SomeStruct { int
| a; int b }". A strong type systems forces you to write some
| sort of adapting code between those two, and guarantees
| that the result of that adapting code will be only and
| exactly the type that comes out of that adapting code, no
| "whoops, sometimes this dynamic code just returns a string,
| or maybe a network socket, or who knows what". Nothing can
| prevent your HTTP API from receiving a JPG of an anime
| character instead of JSON specifying a user to delete, but
| a strong type system _can_ make you deal with that
| immediately and fully, instead of garbage data of
| indeterminate type floating through the system for an
| indeterminate period of time.
|
| [1]: Also note there are a lot of "strong type systems" in
| the world that still fail to take advantage of their own
| capabilities and let bare string types and such float
| around too much. There are reasons why libraries must
| support the lowest common denominator; a file _is_ a series
| of bytes with no further constraints, so the lowest level
| API has no choice but to accept that, but higher level APIs
| should more often take more restricted types. That strong
| type systems _can_ save you from this doesn 't mean they
| all _do_. I have a number of wrapper types in various
| languages just to add these guarantees to my programs not
| provided by the underlying libraries, though I also have
| some code that just wraps the underlying libraries that can
| 't help but correctly take raw bytes at the lowest level.
| tyingq wrote:
| >If you have some function that accepts it, blindly casts
| it to UTF-8
|
| Unfortunately, if you interact with services you didn't
| write, you're usually back to getting "strings" of
| unknown encoding, and typically requirements that force
| some blind or semi-blind guessing.
| tialaramex wrote:
| _If_ the language explicitly says how strings are
| defined, libraries that go "Eh, I'll just shove nonsense
| bytes in this data structure and claim that's a string"
| are broken by definition.
|
| That's just as true in Java as in Rust. The problem is
| languages like C++ or D which just don't care and have a
| "string" type that might just be some bytes.
| tyingq wrote:
| I don't mean libraries, I mean external services.
| Ambiguous strings are everywhere.
| simiones wrote:
| There is no such thing as an "escaped string". Escaping is not
| a general concept, it is something that differs given the
| intended destination of that string. For example, "I am a
| %SYSTEM% person" is a perfectly fine escaped bash string, but
| an unsafe CMD string; it is also fine as a C# format string,
| but potentially unsafe as a C format string, depending on your
| actual implementation of printf; it is an escaped MSSQL filter
| string, but not an escaped PostgresSQL filter string.
|
| Also, not all strings/texts should be thought of as Unicode
| code points/graphemes.
| mjw1007 wrote:
| Sure, you need a different type for each different form of
| escaping you want to track, but that doesn't make the idea
| unworkable.
|
| A type that says (say) "this is a string containing html
| PCDATA" is a useful thing to have.
| simiones wrote:
| The biggest problem appears once you start wanting to
| combine such strings.
|
| Say a user inputs some raw text in a form that is intended
| to be the title of a button in a HTML form that will be
| sent in a JSON file to be stored in a SQL db. The
| expectation is that you can later retrieve this HTML
| snippet from the DB and display it on the screen.
|
| You have to first escape the raw text from the user so that
| it can be safely used in HTML - so you will go from
| user_input_string to html_pcdata_escaped_
| user_input_string. Then you compose a bit of HTML that
| contains the button and this part; let's say you store it
| in some HTML DOM object. Then you want to send this HTML
| object as JSON, so you have to know to convert
| html_pcdata_escaped_ user_input_string into
| json_string_escaped_user_input_string - but that loses type
| information which may hurt us later, so maybe we want to
| actually store it as
| json_string_escaped_html_pcdata_escaped_user_input_string.
| Then, if we want to use this as part of an SQL query
| string, by the same considerations, we want to put it in a
| mysql_like_filter_escaped_json_string_escaped_html_pcdata_e
| scaped_user_input_string - which is getting really ugly,
| and easy to mess up.
|
| Of course, the order of escaping matters, so an mysql_like_
| filter_escaped_json_string_escaped_html_pcdata_escaped_user
| _input_string and a json_string_escaped_mysql_like_filter_e
| scaped_html_pcdata_escaped_user_input_string are different
| things that need to be decoded differently (of course, for
| SQL in particular we could use prepared queries instead).
|
| Also, we can't ever concatenate this with any other string-
| like type until perhaps the final use point (such as
| sending a query string to the DB), since we need to
| remember which part of the string is escaped in which way,
| and for what types of uses it is safe (an HTML-escaped
| string may still contain SQLi or JSON injection).
|
| The point is that even with proper types, this is not easy
| to manage or fix.
|
| It also requires quite advanced type systems to be able to
| use these in normal contexts - say, you want to store
| several such strings with different provenances in a Map or
| Set or even List, without "forgetting" the provenance.
| mjw1007 wrote:
| As far as I'm concerned, "the point is" that what we're
| describing here -- keeping track of the type of each
| piece of data -- is the best way to think about this
| class of problems (as opposed to talking about
| "sanitising" or "safe data", for example).
|
| It's then up to us to decide how to best make use of the
| type system of whatever language we end up implementing
| it in (or, indeed, to treat the ability to deal with this
| well as a requirement when we're choosing a language).
|
| For me, effects like "we can't ever concatenate this with
| any other string-like type" are desirable features, not
| problems with this approach: either it's possible to
| convert both strings to a common form, or I shouldn't be
| trying to combine them.
| simiones wrote:
| Sure - I'm just saying that the article is right that
| this problem is difficult, not easy, and that it doesn't
| get significantly easier if we accurately keep track.
| teddyh wrote:
| (Just for the record, I agree completely, and nothing of what
| I wrote should be construed as contradicting any of that.)
| jerf wrote:
| It really isn't. Proof: Most people who try, largely succeed.
| Those who do something silly like try to do it 100% manually
| generally rapidly realize that's not a good plan, and usually
| there is a not-very-hard way to encapsulate it somehow, since
| that's pretty much what our languages do, encapsulate things.
|
| I'm not saying it's completely trivial or that there's never an
| issue here or there. What I'm saying is, it's on par with any of
| dozens of other issues in programming. Bugs happen, errors
| happen, but no more so than anyone else. A series of systems with
| slightly different encoding practices can also cause some
| headaches, but, again, these are on par with a number of other
| issues that can emerge in such systems, not especially bad. I've
| seen a lot of crappy code that gets this wrong at scale, written
| by programmers who don't really know or care what they're doing,
| but the same code was crap in a dozen other ways too, and
| generally screwed up even easier things as well.
|
| Where you get the problems are, from largest to smallest, 1.
| People who don't realize it's an issue at all and concatenate
| everything and 2. People who have _just_ been taught about it,
| and are doing a wrong thing, most often trying to filter on the
| way "in" instead of the way "out". ("Sanitize user input"
| delenda est. Stop saying it. It's wrong.) Which is _also_ not an
| exceptional case, because again there are any number of things
| that have the exact same characteristics in the programming
| world.
|
| I would expect "ridonkulously hard" to encompass something that
| even when tried is super hard and often a failure, and this isn't
| that case.
___________________________________________________________________
(page generated 2022-11-01 23:02 UTC)