Linux-Namensgebung für physikalische Netzwerk-Devices

Opensuse nutzt ja seit längerem "systemd". Zwei der größten Veränderungen, die damit einhergingen, war die intensive Nutzung von "udev" durch "systemd" im Rahmen des Systemstarts und eine resultierende Veränderung der Namensgebung für physikalische Devices - u.a. Netzwerkdevices. Alles unter dem Schlagwort "predictable device names".

Einen großen Einfluss hatte das u.a. auf Netzwerk-Devices: An die Stelle der alten NIC-Bezeichnungen "eth0", "eth1", etc. traten dann auf meinem Desktop-System z.B. so illustre Ausdrücke wie "enp8s0", "enp9s0" oder noch komplexere Namen.

Aus den netzwerk-bezogenen "udev"-Regeln in den Dateien unter "/lib/udev/rules.d" alleine konnte ich mir bislang nicht erklären, wie es zur Festlegung der Device-Namen kam. "Die "udev"-Regeln betreffen ja etliche unterschiedliche Identifikations-Bezeichnungen. Ich hatte die Namensvergabe daher bislang einfach als Ergebnis einer für mich nicht nachvollziehbaren "udev/systemd"-Logik abgetan.

In der Auseinandersetzung mit virtuellen Bridges bin ich jedoch wieder über das Thema und inzwischen auch über zwei Artikel gestolpert, die das Ganze erklären. Ich möchte an dieser Stelle gerne auf die betreffenden Artikel hinweisen:

Der erste lesenswerte Artikel stammt von Major Hayden, der der Sache mal nachgegangen ist:
https://major.io/2015/08/21/understanding-systemds-predictable-network-device-names/

Besonders hilfreich fand ich in dem zitierten Artikel den Auszug aus "src/udev/udev-builtin-net_id.c", der einem hilft eine Zugang zu einer Namensvergabe wie "enp8s0" zu finden:

  
 * Two character prefixes based on the type of interface:
 *   en -- ethernet
 *   sl -- serial line IP (slip)
 *   wl -- wlan
 *   ww -- wwan
 *
 * Type of names:
 *   b<number>                             -- BCMA bus core number
 *   ccw<name>                             -- CCW bus group name
 *   o<index>[d<dev_port>]                 -- on-board device index number
 *   s<slot>[f<function>][d<dev_port>]     -- hotplug slot index number
 *   x<MAC>                                -- MAC address
 *   [P<domain>]p<bus>s<slot>[f<function>][d<dev_port>]
 *                                         -- PCI geographical location
 *   [P<domain>]p<bus>s<slot>[f<function>][u<port>][..][c<config>][i<interface>]
 *                                         -- USB port number chain

 
Der zweite Artikel betrifft die Regeln, nach denen "systemd" verschiedene Varianten der Namensgebung bis zu einer endgültigen Entscheidung für ein Netzwerk-Device durchexerziert:

http://www.freedesktop.org/wiki/Software/systemd/PredictableNetworkInterfaceNames/

Ich zitiere aus dem Artikel:

  
The following different naming schemes for network interfaces are now supported by udev natively:

    Names incorporating Firmware/BIOS provided index numbers for on-board devices (example: eno1)
    Names incorporating Firmware/BIOS provided PCI Express hotplug slot index numbers (example: ens1)
    Names incorporating physical/geographical location of the connector of the hardware (example: enp2s0)
    Names incorporating the interfaces's MAC address (example: enx78e7d1ea46da)
    Classic, unpredictable kernel-native ethX naming (example: eth0)

By default, systemd v197 will now name interfaces following policy 1) if that information from the firmware is applicable and available, falling back to 2) if that information from the firmware is applicable and available, falling back to 3) if applicable, falling back to 5) in all other cases. Policy 4) is not used by default, but is available if the user chooses so. 


 
Aha - das kann ich nachvollziehen.

Wer sich mit diesem Wissen ausgestattet auch mal ansehen will, was "udev" auf einem System alles an Definitionen für gefundene Netzwerk-Devices hinterlegt, nutze das Kommando

"udevadm info -e"

mit Filtern. Auf meinem Desktop-System etwa finde ich:

   
mytux:~ # udevadm info -e | grep net            
E: ID_MODEL_FROM_DATABASE=RTL8111/8168/8411 PCI Express Gigabit Ethernet Controller (Motherboard)
E: ID_PCI_SUBCLASS_FROM_DATABASE=Ethernet controller
P: /devices/pci0000:00/0000:00:1c.4/0000:08:00.0/net/enp8s0
E: DEVPATH=/devices/pci0000:00/0000:00:1c.4/0000:08:00.0/net/enp8s0
E: ID_MODEL_FROM_DATABASE=RTL8111/8168/8411 PCI Express Gigabit Ethernet Controller (Motherboard)
E: ID_NET_LINK_FILE=/usr/lib/systemd/network/99-default.link
E: ID_PCI_SUBCLASS_FROM_DATABASE=Ethernet controller
E: SUBSYSTEM=net
E: SYSTEMD_ALIAS=/sys/subsystem/net/devices/enp8s0 /sys/subsystem/net/devices/enp8s0

E: ID_MODEL_FROM_DATABASE=RTL8111/8168/8411 PCI Express Gigabit Ethernet Controller (Motherboard)
E: ID_PCI_SUBCLASS_FROM_DATABASE=Ethernet controller
P: /devices/pci0000:00/0000:00:1c.5/0000:09:00.0/net/enp9s0
E: DEVPATH=/devices/pci0000:00/0000:00:1c.5/0000:09:00.0/net/enp9s0
E: ID_MODEL_FROM_DATABASE=RTL8111/8168/8411 PCI Express Gigabit Ethernet Controller (Motherboard)
E: ID_NET_LINK_FILE=/usr/lib/systemd/network/99-default.link
E: ID_PCI_SUBCLASS_FROM_DATABASE=Ethernet controller
E: SUBSYSTEM=net
E: SYSTEMD_ALIAS=/sys/subsystem/net/devices/enp9s0 /sys/subsystem/net/devices/enp9s0

P: /devices/virtual/misc/network_latency
N: network_latency
E: DEVNAME=/dev/network_latency
E: DEVPATH=/devices/virtual/misc/network_latency
P: /devices/virtual/misc/network_throughput
N: network_throughput
E: DEVNAME=/dev/network_throughput
E: DEVPATH=/devices/virtual/misc/network_throughput
N: net/tun
E: DEVNAME=/dev/net/tun

P: /devices/virtual/net/br0
E: DEVPATH=/devices/virtual/net/br0
E: ID_NET_LINK_FILE=/usr/lib/systemd/network/99-default.link
E: SUBSYSTEM=net
E: SYSTEMD_ALIAS=/sys/subsystem/net/devices/br0

P: /devices/virtual/net/lo
E: DEVPATH=/devices/virtual/net/lo
E: ID_NET_LINK_FILE=/usr/lib/systemd/network/99-default.link
E: SUBSYSTEM=net

P: /devices/virtual/net/virbr0
E: DEVPATH=/devices/virtual/net/virbr0
E: ID_NET_LINK_FILE=/usr/lib/systemd/network/99-default.link
E: SUBSYSTEM=net
E: SYSTEMD_ALIAS=/sys/subsystem/net/devices/virbr0

P: /devices/virtual/net/virbr0-nic
E: DEVPATH=/devices/virtual/net/virbr0-nic
E: ID_NET_LINK_FILE=/usr/lib/systemd/network/99-default.link
E: SUBSYSTEM=net
E: SYSTEMD_ALIAS=/sys/subsystem/net/devices/virbr0-nic

P: /devices/virtual/net/virbr1
E: DEVPATH=/devices/virtual/net/virbr1
E: ID_NET_LINK_FILE=/usr/lib/systemd/network/99-default.link
E: SUBSYSTEM=net
E: SYSTEMD_ALIAS=/sys/subsystem/net/devices/virbr1

P: /devices/virtual/net/virbr1-nic
E: DEVPATH=/devices/virtual/net/virbr1-nic
E: ID_NET_LINK_FILE=/usr/lib/systemd/network/99-default.link
E: SUBSYSTEM=net
E: SYSTEMD_ALIAS=/sys/subsystem/net/devices/virbr1-nic

P: /devices/virtual/net/vmnet0
E: DEVPATH=/devices/virtual/net/vmnet0
E: ID_NET_LINK_FILE=/usr/lib/systemd/network/99-default.link
E: INTERFACE=vmnet0
E: SUBSYSTEM=net
E: SYSTEMD_ALIAS=/sys/subsystem/net/devices/vmnet0

P: /devices/virtual/net/vmnet1
E: DEVPATH=/devices/virtual/net/vmnet1
E: ID_NET_LINK_FILE=/usr/lib/systemd/network/99-default.link
E: INTERFACE=vmnet1
E: SUBSYSTEM=net
E: SYSTEMD_ALIAS=/sys/subsystem/net/devices/vmnet1

P: /devices/virtual/net/vmnet2
E: DEVPATH=/devices/virtual/net/vmnet2
E: ID_NET_LINK_FILE=/usr/lib/systemd/network/99-default.link
E: INTERFACE=vmnet2
E: SUBSYSTEM=net
E: SYSTEMD_ALIAS=/sys/subsystem/net/devices/vmnet2

 
(Der erfahrene Leser erkennt, dass hier neben physikalischen Devices qemu-Bridges und auch VMware-Bridges aktiv sind. Für entsprechende Devices werden im Zusammenspiel mit den Generatoren der Virtualisierungs-Tools offenbar Standardnamen vergeben, da eindeutige Vendor- oder "geographische" Bus-Bezeichnungen nicht ermittelbar sind.)

Mein Dank gilt den Verfassern der oben genannten Artikel, die zumindest bei mir eine Verständnislücke geschlossen haben.

Vertical gaps resulting from nested inline block elements inside DIV containers – II – getting control

In the first article of this mini series

Vertical gaps resulting from nested inline block elements inside DIV containers – I

we have seen that under certain circumstances (nested) inline-blocks may lead to vertical gaps between themselves and/or between their lower border and the lower bottom of surrounding DIV containers. (Others may describe it as an unexected spacing or margin). We tried to explain this behavior:

If no other precisely defined height scale is given the (min-) height of the innermost inline element - in our case an inline block - will be interpreted as if a letter of a corresponding font-size were present. We have shown that the baseline for letters indeed is set to the bottom of the innermost inline block if no (relatively positioned) contents of the innermost inline block is given.

In this article we want to play around with our example to get an improved understanding and to find options to control and eliminate the vertical gaps. Our example code is:

<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<title>Test</title>
<style type="text/css">
	
	html {
		font-family: Verdana, Geneva, Arial, Helvetica, sans-serif;
		font-size: 10px;
	}
	
	div {
		position:relative;
		margin:0;
		padding:0;
	}
	
	div.header {
		float:left; 
		min-width: 30.0rem; 
		min-height: 3.2rem; 
		background-color: #DDDDDD;
	}
	
	div.hornav {
		width: auto; 
		min-height: 3.0rem;
		line-height: 3.0rem;
		border: #F00 0.1rem solid;
		background-color: black; 
		text-align: center; 
		
	}
	
	div.hornav_txt {
		display:inline-block; 
		min-width: 20.0rem;  
		min-height:2.8rem;
		line-height: 2.8rem;
		border: solid 0.1rem #FF0; 
		background-color: #FFF;
		text-align:center; 
		
	}
	
	div.mp_cont {
		display:inline-block; 
		min-width:10.0rem; 
		min-height:2.6rem;
		line-height:2.6rem; 
		border:solid 0.1rem #090;
		background-color:#F00; 
		
	}
	
	div.bg{
		position: absolute; 
		min-width:10.0rem; 
		min-height:2.6rem;
		top:0;
		bottom:0;
		left:0;
		right:0;
		border:0;
		background-color:#FF0;
		opacity: 0.5;
		z-index:1;  
	}
	
	div.fg {
		position: absolute; 
		min-width:10.0rem; 
		min-height:2.6rem;
		line-height:2.6rem; 
		border:0;
		z-index:2;
	}
	
</style>
</head>

<body>
        <div class="header">
            <div class="hornav">
                <div class="hornav_txt">
                	<div class="mp_cont">
                		<div class="bg"></div>
                		<div class="fg"></div>
                	</div>
             	</div>
            </div>
        </div> 
        <div class="header" style="margin-left:50px;">
            <div class="hornav">
                <div class="hornav_txt">
                	<div class="mp_cont">
                		<div class="bg"></div>
                		<div class="fg"></div>
                	</div><span style="font-size:2.8rem; ">XÜj</span>jjgg
             	</div><span style="color:yellow;">jg</span>
            </div>
        </div>
        <p style="clear:left;" ></p>
</body>
</html>

Test 1 - line-height reduction for the inline-blocks

If our explanation of the gap occurrence has some truth in it we should see an effect when we reduce the line-height for the inline-blocks in their containers.
So, let us first try the following:

        <div class="header">
            <div class="hornav">
                <div class="hornav_txt" style="line-height:0;">
                	<div class="mp_cont">
                		<div class="bg"></div>
                		<div class="fg"></div>
                	</div>
             	</div>
           </div>
        </div> 

 
Now, our innermost inline-block element div.mp_cont gets no line-height in its container, but it still has a min-height. The result is:

nested_inline_blocks_5

Not quite, what we wanted. At least div.mp_cont shrank to the expected height. But, still, there is no defined inline-block height available and the browser still does not know what to do about the inline element div.hornav. Hence the vertical gap between div.hornav_txt and div.hornav.

Now, we go a step further and repeat our approach one tag-level higher:

        <div class="header">
            <div class="hornav" style="line-height:0;>
                <div class="hornav_txt" style="line-height:0;">
                	<div class="mp_cont">
                		<div class="bg"></div>
                		<div class="fg"></div>
                	</div>
             	</div>
            </div>
        </div>
 

 
We get:

nested_inline_blocks_6

Yes! Now, we have at least found a remedy in case that we need to control the height for inline boxes with empty or absolutely positioned contents. Let us keep this option of line-height reduction in mind!

Test 2 - provide some relatively positioned contents in the innermost inline block

If our gap theory is valid we should provide an innermost inline content with a defined height to get a reasonable vertical adjustment in the end. To achieve this for our example we have to perform 2 steps:

  1. Set the CSS value of "position" for the innermost block element div.fg to "relative" instead of "absolute".
  2. Put some inline text into div.fg

With the first step we fulfill an objective that we already discussed in our first article:

The innermost contents shall control the height expansion of all containers in a dynamical, flexible way. This is especially important for responsive layouts and CMS systems.

So, lets do the following:

        <div class="header">
            <div class="hornav">
                <div class="hornav_txt">
                	<div class="mp_cont">
                		<div class="bg"></div>
                		<div class="fg" style="position:relative;"></div>
                	</div>
             	</div>
            </div>
        </div> 

 
Note that we eliminated the setting "line-heigt:0;" again. We get:

nested_inline_blocks_7
 
A bit frustrating, isn't it? We gained nothing compared with our original example. But our finding is still consistent with our theory - we still have no defined height of an innermost inline element.

So, now let us look at a small wonder of CSS: We just add a text element - a tiny harmless letter
"x" -

        <div class="header">
            <div class="hornav">
                <div class="hornav_txt">
                	<div class="mp_cont">
                		<div class="bg"></div>
                		<div class="fg" style="position:relative;">x</div>
                	</div>
             	</div>
            </div>
        </div>
 

 
and, hey, everything gets fixed in the sense of our original expectations.

nested_inline_blocks_8

Intermediate Summary

If we had started with the very reasonable setting of our last test we would never have noted any problem in our example. So, why all the fuss?

One reason is that there are a lot of articles on the Internet about uncontrollable vertical gaps related to inline block elements. Actually these gaps are understandable and controllable.

The other reason is that in templates (e.g. for CMS systems) the (future) contents of inline-blocks may not be known - and the inline block elements may intentionally be left empty. Then we would run into the described situation of our example. The use of nested inline blocks on the other side makes horizontal centering easy if the width of the innermost contents is not really known.

Two options to get control over the gaps
Now, we have seen 2 remedies that can be applied to avoid gaps between (nested) inline blocks and their containers:

  • Set the line-height in each of the container of an inline-block to zero or a sufficiently small value. (But be careful - this may have an impact on other elements on the same HTML/DOM hierarchy level.)
  • Provide an innermost inline (text) element with a defined height (e.g. given by a font-size). This innermost element may be a "&nbsp;". Ensure that the innermost inline element can have an impact on outer containers via relative (!) positioning.

Of course, you may/must in some situations also combine these options.

Note: The use of a "&nbsp;" may be helpful for (PHP) templates as a dummy contents. Just replace this dummy text during the creation of the real page by real contents - eg. the text of a real menu point.

What about <P>-tags with different font-sizes as the innermost tags?

Ok, up to now we used plain text inside the div.fg-container. What happens when we use

-tags and change the font sizes for these tags? Well, then it may get pretty weird:

A <p> may actually create vertical gaps again - despite the fact there is enough space for it inside its block container div.fg. One of the reasons is that browsers add their own bit of white space to a <p>. This can be controlled by margin- and padding-settings. However, in our case of a a <p> inside inline-blocks even a "margin-top:0; margin-bottom:0;" does not help.

See the following example:

       
	<div class="header" style="margin-left:50px;">
            <div class="hornav">
                <div class="hornav_txt" >
                    <div class="mp_cont" >
                        <div class="bg"></div>
                        <div class="fg">
                            <p style="font-size:1.4rem; margin-top:0rem; margin-bottom:0rem;">Ügj</p>
                        </div>
                    </div>
             	</div>
            </div>
        </div>

 
nested_inline_blocks_9

There is again (!) a small but clearly visible gap at the lower bottom. The inline text-element inside the <p>-tag affects the next inline-block element despite the fact that we have defined normal block element with sufficient vertical space in between. The gap gets bigger with the font-size. See the same example for "font-size:1.6rem;":

nested_inline_blocks_10

For real CSS2/3 experts this may not be too surprising; for me it was.

So, how can we correct this effect? And what do we have to do, when the font-size of the encapsulated

-tag really exceeds the height and line-height of the surrounding container?

The answers are given in the following final code for our example:

<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<title>Test</title>
<style type="text/css">
	
	html {
		font-family: Verdana, Geneva, Arial, Helvetica, sans-serif;
		font-size: 10px;
	}
	
	div {
		position:relative;
		margin:0;
		padding:0;
	}
	
	div.header {
		float:left; 
		min-width: 30.0rem; 
		min-height: 3.2rem; 
		background-color: #DDDDDD;
	}
	
	div.hornav {
		width: auto; 
		min-height: 3.0rem;
		line-height: 0rem;
		border: #F00 0.1rem solid;
		background-color: black; 
		text-align: center; 
		margin:0; 
		padding:0;
	}
	
	div.hornav_txt {
		display:inline-block; 
		position:relative; 
		min-width: 20.0rem;  
		min-height:2.8rem;
		line-height: 0rem;
		border: solid 0.1rem #FF0; 
		background-color: #FFF;
		text-align:center; 
		margin:0; 
		padding:0;
	}
	
	div.mp_cont {
		display:inline-block; 
		position: relative; 
		min-width:10.0rem; 
		min-height:2.6rem;
		line-height: 2.6rem; 
		border:solid 0.1rem #090;
		background-color:#F00;
		margin:0; 
		padding:0;
	}
	
	div.bg{
		position: absolute; 
		min-width:10.0rem; 
		/* line below not required */
		/*min-height:2.6rem;*/
		top:0;
		bottom:0;
		left:0;
		right:0;
		border:0;
		background-color:#FF0;
		opacity: 0.5;
		z-index:1;  
	}
	
	div.fg {
		position:relative; 
		min-width:10.0rem; 
		min-height:2.6rem;
		line-height:2.6rem; 
		border:transparent 0.0rem solid;
		padding:0;
		margin:0;
		z-index:2;
	}
	
</style>
</head>

<body>
        <div class="header">
            <div class="hornav">
                <div class="hornav_txt">
                	<div class="mp_cont">
                		<div class="bg"></div>
                		<div class="fg"> </div>
                	</div>
             	</div>
	    </div>
        </div> 
       <div class="header" style="margin-left:50px;">
            <div class="hornav">
                <div class="hornav_txt" >
                	<div class="mp_cont" >
                		<div class="bg"></div>
                		<div class="fg"><p style="font-size:1.8rem; margin-top:0rem; margin-bottom:0rem;">Ügj</p></div>
		  	</div>
                </div>
	    </div>
        </div>
        <p style="clear:left;"></p>
        <div class="header" style="margin-left:0px; margin-top:50px;">
            <div class="hornav">
                <div class="hornav_txt" >
                	<div class="mp_cont" >
                		<div class="bg"></div>
                		<div class="fg" style="line-height: normal;"><p style="font-size:5.0rem; margin-top:0.6rem; margin-bottom:0.6rem;">Ügj</p></div>
                	</div>
             	</div>
            </div>
        </div>
        <div class="header" style="margin-left:50px; margin-top:50px;">
            <div class="hornav">
                <div class="hornav_txt" >
                	<div class="mp_cont" >
                		<div class="bg"></div>
                		<div class="fg" style="line-height:normal;"><p style="font-size:5.0rem; margin-top:0.6rem; margin-bottom:0.6rem;">Ügj<br>Ügj</p></div>
                	</div>
             	</div>
            </div>
        </div>
        <p style="clear:left;"></p>
        <div class="header" style="margin-left:50px; margin-top:50px; min-width: 50.0rem; ">
            <div class="hornav">
                <div class="hornav_txt" >
                	<div class="mp_cont" >
                		<div class="bg"></div>
                		<div class="fg" style="line-height:normal; padding-left:1.0rem; padding-right:1.0rem;"><p style="font-size:5.0rem; margin-top:0.6rem; margin-bottom:0.6rem;">Hallo Hallo<br>Hallo Hallo Ügj</p></div>
                	</div>
             	</div>
            </div>
        </div>
</body>
</html>

 
What did we change ?

The first thing you may note is that in the CSS definitions we used our first trick:
We explicitly set the line-heights for div.hornav and div.hornav_txt to zero.

The second important point is that when the font gets too large we must adjust the line-height for the container div.fg. We use "line-height:normal;" to adjust the line-height to the height of the included contents.

So, in case you should build a CMS in which the user can choose font-sizes by himself this is something you have to take care about. You may provide a standard setting; but if in situations like in our example case the font size of a <P>-tag exceeds defined limits our page creation program has to modify style settings explicitly to guarantee a flexible height adjustment.

The result is shown in following picture:

nested_inline_blocks_12

This gives us exactly what we wanted:

  • Easy horizontal centering via the use of (nested) inline block elements (even if the content length gets bigger than some threshold).
  • Vertical flexibility - even if we work with font-sizes bigger than a threshold.
  • NO VERTICAL GAPS between (nested) containers of inline-block elements.

It is easy to see how we can use the discussed elements in templates to create horizontally centered menus with menu points for which the horizontal and vertical size is not known.

CSS is a bit weird sometimes - but fun, too!

PHP and web application security – bad statistics and wrong conclusions

Sometimes I have discussions with developers of a company working mainly with Java. As I myself sometimes do development work with PHP I am regarded more or less as a freak in this community. Typical arguments evolve along the lines:

"PHP does not enforce well structured OO code, no 4-layer-architecture built in, problems with scalability", etc.... I do not take these points too seriously. I have seen very badly structured Java OO code, one can with proper techniques implement web services in a kind of logical 3rd layer on special servers and Facebook proves PHP scalability (with some effort), etc...

What is much more interesting for me these days is the question how security aspects fit into the use of different programming languages. And here comes the bad news - at least according to statistics published recently by the online magazine Hacker News - see
http://thehackernews.com/2015/12/programming-language-security.html

The statistics on OWASP 10 vulnerability types of the investigated PHP code looks extremely bad there - compared to the investigated Java code examples. I admit that this is an interesting result for hackers and that it is somewhat depressing for security aware PHP developers.

However, is this the bad statistics the fault of the programming language?

I doubt it - despite the fact that the named article recommends to "Choose Your Scripting Language Wisely". I would rather recommend: Educate your PHP developers properly and regularly, implement a proper quality assurance with special security check steps based on vulnerability scanning tools and invest in regular code reviews. Why?

The investigation revealed especially large deviations in the fields of XSS, SQL-injection, command injection (major elements on the OWASP 10 list). The countermeasures against the named attack vectors for PHP are all described in literature and very well known (see e.g. the books "PHP Sicherheit" of C.Kunz, S.Esser or "Pro PHP Security" of Snyder, Myer, Southwell). One of the primary key elements of securing PHP applications against attacks of the named types is a thorough inspection, analysis and correction treatment of submitted GET/POST-parameters (and avoidance of string parameters wherever possible). Never trust any input and escape all output! Define exceptions wisely and rewrite sensitive string elements according to your rules. Check whether input really comes from the right origin - e.g. your own domain, etc., etc.

Whether all relevant security measures are implemented in the PHP code of a web application has therefore more to do with the mentality, technical ability, the knowledge and on the negative side with the laziness of the programmer than with the programming language itself. As at least the technical capability is a matter of education, I conclude:

Tests regarding type, value range, length and of course tests of the contents of received string variables and e.g. image source references plus sanitizing/elimination/deactivation of problematic contents as well as the proper use of respective available library functions for such tests should be part of regular PHP training programs. In addition the use of web application scanning tools like OWASP's ZAP scanner or the Burp Suite Pro (if you have money to afford the latter) should be trained. Such tools should become part of the QA chain. As well as educated penetration testers with the perspective of the attacker .... The money a SW-company invests for such educational measures is well invested money.
See for the significant impact of education e.g.:
https://seclab.stanford.edu/websec/scannerPaper.pdf

I would regard the statistical results discussed in the "Hacker News" article much more conclusive if we were provided additional information about the type of applications analyzed and also the size and type of the companies behind the application development. And whether and what type of QA efforts were used. This would give a much better indication of why the Java code showed more quality regarding the prevention of OWASP 10 attacks. One reason could e.g. be that Java applications very often are developed for enterprise purposes - and bigger companies typically invest more time and effort into QA ...

So, another valid interpretation of the presented statistics could be that the QA for typical PHP web application SW is on average worse than for Java SW. I admit that such a finding would also be very interesting - but it does not prove that one cannot write secure Web applications with PHP or that the production of secure code is for whatever reasons easier with Java.

In addition the presented number of bugs per MB itself is questionable: if you only look at 3 bad PHP examples and 1 good Java example you may get the same type statistics - but it would be totally meaningless. The distribution of PHP-, Java-, JS-code etc. among the statistical sample is, however, nowhere discussed in the named article - neither in number of applications nor in MB percentages.

Therefore: Without further information the implied conclusion that already the proper choice of a web scripting language would help to improve security of web applications appears is misleading.

To improve the mood of PHP developers having read the article in "Hacker News": Have a look at the results of a similar investigation presented at this link
http://info.whitehatsec.com/rs/whitehatsecurity/images/statsreport2014-20140410.pdf

See also:
https://blog.whitehatsec.com/a-security-experts-thoughts-on-whitehat-securitys-2014-web-stats-report/
https://www.scriptrock.com/blog/which-web-programming-language-is-the-most-secure
https://threatpost.com/security-begins-with-choice-of-programming-language/105441/

It may help, really !

[But keep in mind: Only trust statistics you have manipulated yourself.]